I have facing thread issue in the below code.When then thread executes the Run method of the runnable object,it doesnt print the data that I expect it to be.
code 1--calling code
Map<String,Object> logData = CPEMethodData.getLogDataMap();
CatalogUpdaterLogger.getLogger().info("6 before new splunk logger log data =" + logData);
CatalogrLogger writer = new CatalogLogger(LogType.INFO,logData,LoggerType.CATALOGUPDATER);
LogPool.INSTANCE.submitTask(writer);//submitting writer which is a runnable object to the queue
//add one more task/writer to the queue in the same method
logData = CPEMethodData.getLogDataMap();
CatalogUpdaterLogger.getLogger().info("11 before 3rd writer=logData "+logData);
CatalogLogger writer2 = new CatalogLogger(LogType.INFO,logData,LoggerType.CATALOGUPDATER);
LogPool.INSTANCE.submitTask(writer2);
In the above code,I have checked that logData Returned by the CPEMethodData.getLogDataMap()is different which I expected.But still when the runnable object actually executes,it runs with same data...
code 2--creating thread pool with 5 threads...
public enum LogPool {
INSTANCE;
private static final int nThreads = 5;
final ExecutorService executor = Executors.newFixedThreadPool(nThreads);
public synchronized void submitTask(Runnable task) {
executor.execute(task);
}
Code 3--runnable code
public class CatalogLogger implements Runnable {
protected LogType logType;
protected LoggerType loggerType;
protected Map<String, Object> logData;
public CatalogLogger(LogType logType, Map<String, Object> logData,
LoggerType loggerType) {
this.logType = logType;
this.logData = logData;
this.loggerType = loggerType;
}
public void run() {
System.out.println("running with logData " + logData);
System.out.println(" Thread.currentThread().hashCode() " +Thread.currentThread().hashCode());
switch (loggerType) {
case ORDERPROCESSING:
logData(Logger.getLogger(ORDER_LOG));
break;
case CATALOGUPDATER:
logData(Logger.getLogger(CATALOGUPDATER_LOG));
break;
}
}
Below is the CPEmethoddata.getLogData
public class CPEMethodData {
private static ThreadLocal<Map<String, Object>> logDataMap = new ThreadLocal<Map<String, Object>>();
public static Map<String,Object> getLogDataMap() {
return logDataMap.get();
}
public static void setOppParameters(Map<String, Object> inputParams) {
Map<String, Object> oppStatus = logDataMap.get();
if (oppStatus == null) {
oppStatus = new HashMap<String, Object>();
logDataMap.set(oppStatus);
}
oppStatus.put(INPUT_PARAMS, inputParams);
}
#SuppressWarnings("unchecked")
public static Map<String, Object> getOperationParameters() {
Map<String, Object> oppStatus = logDataMap.get();
if (oppStatus != null)
return (Map<String, Object>) oppStatus.get(INPUT_PARAMS);
return null;
}
}
when I run the code 1 which submits two runnable to the queue,I expect to see different logData content in the sysout of the run method but as i have debugged it I see that data is same in both the executions...seems that 2nd runnable is interfering with the first one....Can anyone please help me to understand what is the problem here.I thought I am passing 2 different instances of CatalogLogger and shouldnt cause any problem..Also can anyone please suggest any solution for this ?
As written by the #ReneLink in the comment to my question ,CPEMethodData.getLogDataMap was returning same instance of the hashmap...So by the time thread's run method was getting executed hashmap's content were getting modified.I created deep copy of the hashmap using Cloner facility and passed the same to the thread.
Thanks #ReneLink for pointing out this to me.
Related
I'm trying to count for each minute the records with the same type as in the parameter in a List, in a given time range. Using the following Method:
public Map<String, Object> getCountPerMinuteForType(final String type,
final long startTimestamp,
final long endTimestamp) {
final Map<String, Object> countsPerMinForType = new HashMap<>();
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
int counter = 0;
List<Data> dataList = storage.retrieveData();
for(Data data: dataList){
if (data.getType().equals(type) &&
data.getUnixTimestamp() >= startTimestamp &&
data.getUnixTimestamp() <= endTimestamp){
counter++;
}
}
countsPerMinForType.put(type, counter);
}, 0, 1, TimeUnit.MINUTES);
return countsPerMinForType;
}
The problem is, this method returns an empty Map.
When I print the contents of the Map inside the Executors, I can see that it has data.
It happens because an another thread is performing put operation in the Map. The main thread starts execution of the thread and then returns back to the place where it was called. To solve this issue, you may need to create a listener interface which gets called whenever the second thread performs the task.
Here, below is the code sample which you can use and modify according to your needs.
class Test implements Listener {
private Listener listener;
public Test() {
listener = this; //Set this class as your listener
}
//Make your function return nothing
public void getCountPerMinuteForType(final String type, final long startTimestamp,
final long endTimestamp) {
final Map<String, Object> countsPerMinForType = new HashMap<>();
ScheduledExecutorService service =
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
int counter = 0;
List<Data> dataList = storage.retrieveData();
for (Data data : dataList) {
if (data.getType().equals(type) &&
data.getUnixTimestamp() >= startTimestamp &&
data.getUnixTimestamp() <= endTimestamp) {
counter++;
}
}
listener.onNewData(type, counter);
countsPerMinForType.put(type, counter);
}, 0, 1, TimeUnit.MINUTES);
//return countsPerMinForType;
//If service is terminated call the listener and perform your operation there
if (service.isTerminated()) {
listener.dataFilled(countsPerMinForType);
}
}
#Override
public void onNewData(String str, Object obj) {
//Perform your task here
}
#Override
public void dataFilled(Map<String, Object> data) {
//Perform your task here
}
}
interface Listener {
void dataFilled(Map<String, Object> data);
void onNewData(Map<String, Object> data);
}
The issue you have is that you're expecting the thread you're spinning off has completed the work and the results populated in the countsPerMinForType are returned. This is not what is happening...
What is happening is:
you call the method from the main/current thread of execution
the Map is created
a new thread is spun off to do some work
almost immediately, the method returns and the map is still empty.
...
after the method has completed, the work being performed by the spun off thread is then carried out... and subsequently the calling method never sees the result.
You can confirm this is the case with a test that returns a timestamp for when the getCountPerMinuteForType starts and ends, and another timestamp for when the Thread starts and ends. The start times will be in order, the end times will not be in order.
Also, you may want to consider using a ConcurrentHashMap for a multi-threaded application.
This question already has an answer here:
Java variables overwriting
(1 answer)
Closed 2 years ago.
I have task implementing Runnable, which need to populate ArrayList with custom classes extending TimerTask.
My code:
public class ClusterSessionTask implements Runnable {
#Override
public void run() {
CheckTask checkTask = null;
Map<String, String> keyspaces = getKeyspaces(cluster_name);
for (Map.Entry<String, String> key_to_colfam : keyspaces.entrySet()) {
String k = key_to_colfam.getKey();
String c = key_to_colfam.getValue();
checkTask = new CheckTask(k, c, session);
tasks.add(checkTask);
}
}
}
You can see I am initialising new CheckTask every iteration in the for loop, but I am still getting the previous elements in the list being overwritten.
See this debug info:
on the 1st iteration you can see the task a:b
But on the second one a:b is being overwritten by the new task c:d:
CheckTask is a class extending TimerTask:
class CheckTask extends TimerTask {
private static int i = 0;
private static String keyspace;
private static String colfam;
private static CqlSession session;
CheckTask(String k, String c, CqlSession s) {
keyspace = k;
colfam = c;
session = s;
}
public void run() {
...
}
#Override
public String toString() {
return keyspace + ":" + colfam;
}
}
What am I doing wrong? it is somehow passing the object by reference and the new commend being ignored?
Thanks
Problem is the static keyword for keyspace,colfam,session in the constructor.
Even if you create 2 instances , they would share same value
Below is my class which uses CountDownLatch to make sure reads are not happening on the primary, secondary and tertiary maps for the first time whenever writes are happening on those maps.
public class ClientData {
public static class Mappings {
public final Map<String, Map<Integer, String>> primary;
public final Map<String, Map<Integer, String>> secondary;
public final Map<String, Map<Integer, String>> tertiary;
public Mappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
this.primary = primary;
this.secondary = secondary;
this.tertiary = tertiary;
}
}
private static final AtomicReference<Mappings> mappings = new AtomicReference<>();
private static final CountDownLatch hasBeenInitialized = new CountDownLatch(1);
public static Mappings getMappings() {
try {
hasBeenInitialized.await();
return mappings.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e);
}
}
public static void setMappings(
Map<String, Map<Integer, String>> primary,
Map<String, Map<Integer, String>> secondary,
Map<String, Map<Integer, String>> tertiary
) {
setMappings(new Mappings(primary, secondary, tertiary));
}
public static void setMappings(Mappings newMappings) {
mappings.set(newMappings);
hasBeenInitialized.countDown();
}
}
And below is my background thread class which is only responsible for setting all the three maps (look for parseResponse method below). It runs every 10 minutes.
public class TempBackgroundThread {
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
Map<String, Map<Integer, String>> primaryTables = null;
Map<String, Map<Integer, String>> secondaryTables = null;
Map<String, Map<Integer, String>> tertiaryTables = null;
//...
// store the three map data in ClientData class variables if anything has changed
// which can be used by other threads, this will be updated once every four or five months
if(changed) {
ClientData.setMappings(primaryTables, secondaryTables, tertiaryTables);
}
}
}
Problem Statement:
If I am doing all sort of null or sanity checks on my mappings object and primary, secondary and tertiary maps, peformance degrades a lot (not sure why). But if I don't do any sanity or null checks, performance comes very good. Can anyone explain me what's wrong and why it happens?
Below is an example -
I am using ClientData class to get all the mappings in my main thread. As you can see below, I am doing all sort of sanity checks to make sure mappings, mappings.primary, mappings.secondary and mappings.tertiary are not empty. If they are empty, then log an error and return
class Task implements Callable<String> {
public Task() {
}
public String call() throws Exception {
int compId = 100;
String localPath = "hello";
String remotePath = "world";
Mappings mappings = ClientData.getMappings();
if (MyUtilityClass.isEmpty(mappings)
|| (MyUtilityClass.isEmpty(mappings.primary) && MyUtilityClass
.isEmpty(mappings.secondary))
|| MyUtilityClass.isEmpty(mappings.tertiary)) {
// log error and return
}
// otherwise extract values from them
String localPAddress = null;
String remotePAddress = null;
if (MyUtilityClass.isNotEmpty(mappings.primary)) {
String localPId = mappings.primary.get(localPath).get(compId);
localPAddress = mappings.tertiary.get(localPath).get(
Integer.parseInt(localPId));
String remotePId = mappings.primary.get(remotePath).get(compId);
remotePAddress = mappings.tertiary.get(remotePath).get(
Integer.parseInt(remotePId));
}
String localSAddress = null;
String remoteSAddress = null;
if (MyUtilityClass.isNotEmpty(mappings.secondary)) {
String localSId = mappings.secondary.get(localPath).get(compId);
localSAddress = mappings.tertiary.get(localPath).get(
Integer.parseInt(localSId));
String remoteSId = mappings.secondary.get(remotePath).get(compId);
remoteSAddress = mappings.tertiary.get(remotePath).get(
Integer.parseInt(remoteSId));
}
// now use - localPAddress, remotePAddress, localSAddress and remoteSAddress
}
}
With the above sanity and null checks on primary, secondary and tertiary mappings, overall performance (95th percentile) of application comes as 4 ms.
But if I do it like this without any sanity checks or null checks on primary, secondary and tertiary mappings, I get overall perforamnce (95th percentile) as 0.87 ms.
class Task implements Callable<String> {
public Task() {
}
public String call() throws Exception {
int compId = 100;
String localPath = "hello";
String remotePath = "world";
Mappings mappings = ClientData.getMappings();
String localPId = mappings.primary.get(localPath).get(compId);
String localPAddress = mappings.tertiary.get(localPath).get(Integer.parseInt(localPId));
String remotePId = mappings.primary.get(remotePath).get(compId);
String remotePAddress = mappings.tertiary.get(remotePath).get(Integer.parseInt(remotePId));
String localSId = mappings.secondary.get(localPath).get(compId);
String localSAddress = mappings.tertiary.get(localPath).get(Integer.parseInt(localSId));
String remoteSId = mappings.secondary.get(remotePath).get(compId);
String remoteSAddress = mappings.tertiary.get(remotePath).get(Integer.parseInt(remoteSId));
// now use - localPAddress, remotePAddress, localSAddress and remoteSAddress
}
}
Below is my isEmpty and isNotEmpty method -
public static boolean isNotEmpty(Object obj) {
return !isEmpty(obj);
}
public static boolean isEmpty(Object obj) {
if (obj == null)
return true;
if (obj instanceof Collection)
return ((Collection<?>) obj).size() == 0;
final String s = String.valueOf(obj).trim();
return s.length() == 0 || s.equalsIgnoreCase("null");
}
See how often your code gets to this point. This can be expensive with some complex objects and their heavy #toString() methods:
final String s = String.valueOf(obj).trim();
Also it creates temporary garbage that might cause Garbage Collection while your test is counting.
Your sanity checks ensure that no code is executed unless everything is perfect. If any of the checks fail, you log and then return. Only if the sanity checks succeed, then you proceed.
Instead, consider proceeding blindly, on the assumption that everything is fine. But surround each critical pieces with a try-catch. In the catches, you then check for the specific error, only for the sake of an accurate exception message.
For example, never do this:
public void getStringLength(String string_thatShouldNeverBeNull) {
Objects.requireNonNull(string_thatShouldNeverBeNull, "string_thatShouldNeverBeNull");
return string_thatShouldNeverBeNull.length();
}
When you can do this instead:
public void getStringLength(String string_thatShouldNeverBeNull) {
try {
return string_thatShouldNeverBeNull.length();
} catch(NullPointerException npx) {
throw new NullPointerException("string_thatShouldNeverBeNull");
}
}
The reason is that the output/error-response is always the same for both of these functions, whether the parameter is null or not. So why do the extra check when it is likely to be valid most of the time? It's wasteful.
There are some situations where this not possible. An example:
public void setTheString(String string_thatShouldNeverBeNull) {
Objects.requireNonNull(string_thatShouldNeverBeNull, "string_thatShouldNeverBeNull");
str = string_thatShouldNeverBeNull;
}
There's never an opportunity for the error to be thrown, so the check must be done.
Below is my factory code which starts the background thread TempScheduler -
public class TempClientFactory {
public static IClient getInstance() {
new TempScheduler().startScheduler();
return ClientHolder.INSTANCE;
}
private static class ClientHolder {
private static final TempClient INSTANCE = new TempClient();
}
}
Now customer will call our code using the above factory like this. They will get our Client instance only once and then use that instance going forward to make a call to a read method in my implementation -
IClient client = TempClientFactory.getInstance();
String response = client.read(userId);
And below is my background thread code which will get the data from the URL, parse it and store it in a class variable -
public class TempScheduler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startScheduler() {
final ScheduledFuture<?> taskHandle = scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
callServers();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}, 0, 10, TimeUnit.MINUTES);
}
}
// call the servers and get the data and then parse
// the response.
private void callServers() {
String url = "url";
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
parseResponse(response);
}
// parse the response and store it in a variable
private void parseResponse(String response) {
//...
ConcurrentHashMap<String, Map<Integer, String>> primaryTables = null;
//...
// store the data in ClientData class variables which can be
// used by other threads
ClientData.setPrimaryMapping(primaryTables);
}
}
After parsing the data coming from the URL, my above background thread will store the result in my class ClientData in its variables by using its setters. Below is my ClientData class.
public class ClientData {
private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
public static Map<String, Map<Integer, String>> getPrimaryMapping() {
return primaryMapping.get();
}
public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
primaryMapping.set(map);
}
}
Problem Statement:-
Now the interesting part comes, As you can see customer will call our code using the above factory and as soon as they will make a call to getInstance() method, my background thread gets started up which will fetch the data from the URL and then parse it and store it in a class variable. But after the getInstance() method is executed they will make a call to read method in my Client code instantly..
And then I am supposed to use the variable which was set by my background thread in my implementation code. My implementation code which has read method uses CallableTaks and future.get so it will automatically comes to call method below. In the below class I am supposed to use the variable value which was set by my background thread. I need to use getPrimaryMapping method to get the value in my below code.
public class ClientTask implements Callable<String> {
private String userId = null;
public ClientTask(String userId) {
this.userId = userId;
}
#Override
public String call() throws Exception {
//.....
String hostname = ClientData.getPrimaryMapping("some_string").get(some_number);
//....
}
}
The problem with this approach is, as soon the customer calls our factory for the first time to get the instance, then it will start the background thread which will fetch the data from the URL and parse it and store the data in a class variable but it will call the read method of my implementation class instantly..
Now it might be possible that for the first time, my background thread is still parsing the data but the actual call has come inside the call method and then it will try to get the data from getPrimaryMapping method but it won't have anything, right? Why bcoz it is still parsing the data.. So how do I make sure that for the first time whenever customer is calling our code, once the parsing is done, then only allow to get the variable value in the call method.
And then the second time will be fine.. As the data will be in memory, only the first time is the problem..
Is there any way to do this?
It's not an elegant solution, but a CountDownLatch could do the trick:
public class ClientData {
private static final AtomicReference<Map<String, Map<Integer, String>>> primaryMapping = new AtomicReference<>();
private static final CountDownLatch firstSet = new CountDownLatch(1);
public static Map<String, Map<Integer, String>> getPrimaryMapping() {
try { firstSet.await(); } catch (Exception ignored) {}
return primaryMapping.get();
}
public static void setPrimaryMapping(Map<String, Map<Integer, String>> map) {
primaryMapping.set(map);
firstSet.countDown();
}
}
I had posted somewhat similar question before also. I got clarification for my doubts as well. But still I need something more. The Hashmap will be initialized with the enum object as the key and a threadpool instance as the value. I am confused as of how to initialize the HashMap for every object been called by some other process ..To make clear :
My program, MyThreadpoolExcecutorPgm.java initializes a HashMap
My Progran AdditionHandler.java requests a thread from the HashMap by passing ThreadpoolName (enum). I am getting "No thread available from HashMap" message. Please do help me.
Below given is my code:
public class MyThreadpoolExcecutorPgm {
enum ThreadpoolName {
DR, BR, SV, MISCELLENEOUS;
}
private static String threadName;
private static HashMap<ThreadpoolName, ThreadPoolExecutor>
threadpoolExecutorHash;
public MyThreadpoolExcecutorPgm(String p_threadName) {
threadName = p_threadName;
}
public static void fillthreadpoolExecutorHash() {
int poolsize = 3;
int maxpoolsize = 3;
long keepAliveTime = 10;
ThreadPoolExecutor tp = null;
threadpoolExecutorHash = new HashMap<ThreadpoolName, ThreadPoolExecutor>();
for (ThreadpoolName poolName : ThreadpoolName.) // failing to implement
{
tp = new ThreadPoolExecutor(poolsize, maxpoolsize, keepAliveTime,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
threadpoolExecutorHash.put(poolName, tp);
}
}
public static ThreadPoolExecutor getThreadpoolExcecutor(
ThreadpoolName poolName) {
ThreadPoolExecutor thread = null;
if (threadpoolExecutorHash != null && poolName != null) {
thread = threadpoolExecutorHash.get(poolName);
} else {
System.out.println("No thread available from HashMap");
}
return thread;
}
}
AdditionHandler.java
public class AdditionHandler{
public void handle() {
AddProcess setObj = new AddProcess(5, 20);
ThreadPoolExecutor tpe = null;
ThreadpoolName poolName =ThreadpoolName.DR; //i am using my enum
tpe = MyThreadpoolExcecutorPgm.getThreadpoolExcecutor(poolName);
tpe.execute(setObj);
}
public static void main(String[] args) {
AdditionHandler obj = new AdditionHandler();
obj.handle();
}
}
I suspect you're just looking for the static values() method which is added to every enum:
for (ThreadpoolName poolName : ThreadpoolName.getValues())
Alternatively, you can use EnumSet.allOf():
for (ThreadpoolName poolName : EnumSet.allOf(ThreadpoolName.class))
(As Bozho says, EnumMap is a good alternative here. You still need to loop through the enum values.)
First, you'd better use EnumMap. Then make sure you have filled the map before you invoked the method.
You can iterate through enum values by one of (in descending order of preference)
for(Enum value : Enum.values())
for(Enum value : EnumSet.allOf(Enum.class))
for(Enum value : Enum.class.getEnumConstants())
But you should also be using an EnumMap.