Empty Map when using Executors - java

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.

Related

Does synchronisation with ConcurrentHashMap's .compute() guarantee visibility?

Within ConcurrentHashMap.compute() I increment and decrement some long value located in shared memory. Read, increment/decrement only gets performed within compute method on the same key.
So the access to long value is synchronised by locking on ConcurrentHashMap segment, thus increment/decrement is atomic. My question is: Does this synchronisation on a map guarantee visibility for long value? Can I rely on Map's internal synchronisation or should I make my long value volatile?
I know that when you explicitly synchronise on a lock, visibility is guaranteed. But I do not have perfect understanding of ConcurrentHashMap internals. Or maybe I can trust it today but tomorrow ConcurrentHashMap's internals may somehow change: exclusive access will be preserved, but visibility will disappear... and it is an argument to make my long value volatile.
Below I will post a simplified example. According to the test there is no race condition today. But can I trust this code long-term without volatile for long value?
class LongHolder {
private final ConcurrentMap<Object, Object> syncMap = new ConcurrentHashMap<>();
private long value = 0;
public void increment() {
syncMap.compute("1", (k, v) -> {
if (++value == 2000000) {
System.out.println("Expected final state. If this gets printed, this simple test did not detect visibility problem");
}
return null;
});
}
}
class IncrementRunnable implements Runnable {
private final LongHolder longHolder;
IncrementRunnable(LongHolder longHolder) {
this.longHolder = longHolder;
}
#Override
public void run() {
for (int i = 0; i < 1000000; i++) {
longHolder.increment();
}
}
}
public class ConcurrentMapExample {
public static void main(String[] args) throws InterruptedException {
LongHolder longholder = new LongHolder();
Thread t1 = new Thread(new IncrementRunnable(longholder));
Thread t2 = new Thread(new IncrementRunnable(longholder));
t1.start();
t2.start();
}
}
UPD: adding another example which is closer to the code I am working on. I would like to remove map entries when no one else is using the object. Please note that reading and writing of the long value happens only inside of remapping function of ConcurrentHashMap.compute:
public class ObjectProvider {
private final ConcurrentMap<Long, CountingObject> map = new ConcurrentHashMap<>();
public CountingObject takeObjectForId(Long id) {
return map.compute(id, (k, v) -> {
CountingObject returnLock;
returnLock = v == null ? new CountingObject() : v;
returnLock.incrementUsages();
return returnLock;
});
}
public void releaseObjectForId(Long id, CountingObject o) {
map.compute(id, (k, v) -> o.decrementUsages() == 0 ? null : o);
}
}
class CountingObject {
private int usages;
public void incrementUsages() {
--usages;
}
public int decrementUsages() {
return --usages;
}
}
UPD2: I admit that I failed to provide the simplest code examples previously, posting a real code now:
public class LockerUtility<T> {
private final ConcurrentMap<T, CountingLock> locks = new ConcurrentHashMap<>();
public void executeLocked(T entityId, Runnable synchronizedCode) {
CountingLock lock = synchronizedTakeEntityLock(entityId);
try {
lock.lock();
try {
synchronizedCode.run();
} finally {
lock.unlock();
}
} finally {
synchronizedReturnEntityLock(entityId, lock);
}
}
private CountingLock synchronizedTakeEntityLock(T id) {
return locks.compute(id, (k, l) -> {
CountingLock returnLock;
returnLock = l == null ? new CountingLock() : l;
returnLock.takeForUsage();
return returnLock;
});
}
private void synchronizedReturnEntityLock(T lockId, CountingLock lock) {
locks.compute(lockId, (i, v) -> lock.returnBack() == 0 ? null : lock);
}
private static class CountingLock extends ReentrantLock {
private volatile long usages = 0;
public void takeForUsage() {
usages++;
}
public long returnBack() {
return --usages;
}
}
}
No, this approach will not work, not even with volatile. You would have to use AtomicLong, LongAdder, or the like, to make this properly thread-safe. ConcurrentHashMap doesn't even work with segmented locks these days.
Also, your test does not prove anything. Concurrency issues by definition don't happen every time. Not even every millionth time.
You must use a proper concurrent Long accumulator like AtomicLong or LongAdder.
Do not get fooled by the line in the documentation of compute:
The entire method invocation is performed atomically
This does work for side-effects, like you have in that value++; it only works for the internal data of ConcurrentHashMap.
The first thing that you miss is that locking in CHM, the implementation has changed a lot (as the other answer has noted). But even if it did not, your understanding of the:
I know that when you explicitly synchronize on a lock, visibility is guaranteed
is flawed. JLS says that this is guaranteed when both the reader and the writer use the same lock; which in your case obviously does not happen; as such no guarantees are in place. In general happens-before guarantees (that you would require here) only work for pairs, for both reader and writer.

Resolving Java thread visibility and concurrency error using Map compute

I use Java 8. I have an event handler that accepts events with a high rate, (n per second) and I want to flush them out to storage when I get so many of them (in this simplified example 1000)
Do I have a visibility error on line 25 myCache.get(event.getKey()).add(event.getBean()); ?
Should I synchronize on handleEvent() method?
public class myClass extends MySimpleEventHanlder {
private Map<String, List<MyBean>> myCache;
private ScheduledExecutorService scheduler;
public void MyClass() {
myCache = new ConcurrentHashMap<String, List<MyBean>>();
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
for (Iterator<Map.Entry<String, List<MyBean>>> it = myCache.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, List<MyBean>> entry = it.next();
if (entry.getValue().size() >= 1000) {
it.remove();
//do some more processing , flush to storage
}
}
}, 0, 60, TimeUnit.SECONDS);
}
#Override
public void handleEvent(Event event) {
if (myCachetCache.containsKey(event.getKey())) {
myCache.get(event.getKey()).add(event.getBean());
}
else{
List<MyBean> beans = new ArrayList<MyBeans>();
beans.add(event.getBean());
myCache.put(event.key, beans);
}
}
}
You definitely have visibility problems: you add items into an ArrayList in one thread, and read the size() from that ArrayList in another thread, with no synchronization in between.
Another problem is that the key may get removed between the calls to myCache.containsKey and myCache.get. This would cause a NullPointerException. That could be solved by using compute, which is guaranteed to be atomic.
myCache.compute(event.getKey(), (key, value) -> {
if (value == null) {
value = new ArrayList<>();
}
value.add(event.getBean());
return value;
});

Adding to a message queue if more important messages come in

We need to send messages with highest priority first so we use a PriorityQueue for our purpose.
PriorityQueue<MessageData> queue = new PriorityQueue<MessageData>();
However, we also want our queue to behave like a sorted set as well. Therefore, we adapt the PriorityQueue to ignore insertions which repeat existing members.
import java.util.Comparator;
import java.util.PriorityQueue;
public class PrioritySet<E> extends PriorityQueue<E> {
private static final long serialVersionUID = 34658778L;
public PrioritySet() {
super();
}
public PrioritySet(int initialCapacity, Comparator<? super E> comparator) {
super(initialCapacity, comparator);
}
#Override
public boolean offer(E e) {
boolean isAdded = false;
if(!super.contains(e)) {
isAdded = super.offer(e);
}
return isAdded;
}
}
Now our app specific implementation of the data structure.
import java.util.Comparator;
public class MessagePrioritySet extends PrioritySet<MessageData> {
private static final long serialVersionUID = 34658779L;
private int minPriorityNumber;
public MessagePrioritySet() {
super();
}
public MessagePrioritySet(int initialCapacity, Comparator<MessageData> comparator) {
super(initialCapacity, comparator);
}
public synchronized int getMinPriorityNumber() {
return minPriorityNumber;
}
public synchronized void setMinPriorityNumber(int minPriorityNumber) {
this.minPriorityNumber = minPriorityNumber;
}
#Override
public synchronized boolean offer(MessageData notification) {
boolean isAdded = super.offer(notification);
if (notification.getPriority() < minPriorityNumber)
minPriorityNumber = notification.getPriority();
return isAdded;
}
public synchronized void reportSent(MessageData notification) {
MessageData nextMessageData = peek();
if (nextMessageData == null)
minPriorityNumber = 0;
else if (nextMessageData.getPriority() > notification.getPriority())
minPriorityNumber = nextMessageData.getPriority();
}
}
Here, we want the data structure to be aware of the minimum priority value of the messages so we declare an instance variable for that. The priority of the incoming message is checked and if this priority is lower than the stored value, the value stored is updated. The use of the class is required to report any sent messages. If no other member of the data structure has a priority as low as the one being removed, then the next element's priority becomes the stored priority.
Two threads share the implemented queue. One thread fetches data from the database and inserts them into the queue. The other reads the queue and sends the highest priority message with the lowest priority number. Because the queue sets the minimum priority value to 0 and the thread which fetches data from the database reads rows with priority value lower than or equal to the minimum value stored in the queue if the stored minimum value is not zero, we can be pretty sure that while the current messages in the queue are being sent, only the new messages which are more important than those already in the queue will be added to the queue.
We think that the operations in the while loops in the threads should be atomic and would thank anyone who could tell us how to make them atomic.
private void startMptSender() {
sleepInterval = 1000;
final MessagePrioritySet messagePrioritySet = new MessagePrioritySet();
Runnable mptReader = new Runnable() {
#Override
public void run() {
while (true) {
List<MessageData> messageDataList;
if (messagePrioritySet.getMinPriorityNumber() == 0)
messageDataList = messageDao.readSMSMpt();
else
messageDataList = messageDao.readSMSMpt(messagePrioritySet.getMinPriorityNumber());
for (MessageData messageData : messageDataList) {
messagePrioritySet.offer(messageData);
}
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException ie) {
}
}
}
};
executor.execute(mptReader);
Runnable mptPusher = new Runnable() {
#Override
public void run() {
while (status) {
if (messagePrioritySet.size() > 0) {
while (messagePrioritySet.size() != 0) {
MessageData noti = messagePrioritySet.remove();
mptSender.sendSms(noti);
messageDao.markNotificationAsRead(noti.getSyskey());
messagePrioritySet.reportSent(noti);
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
executor.execute(mptPusher);
}
}
I assume what you mean with atomic translates to: you want that each thread is doing all of its work for one iteration without being interrupted by the other thread.
In other words: you have (probably multiple) operations; and while thread A is doing his operations, thread B shouldn't be doing anything - because you want to make sure that B only sees the "complete set" of updates made by A.
Sure, when that operation would be just about writing to one int for example, you could be using AtomicInteger for example. But when you are talking about several operations ... you need something else.
A "brute force" solution would be to add some sort of locking. Meaning: your threads share some LOCK object; and whenever one thread enters a "critical section" ... it needs to acquire that LOCK first (and of course release directly afterwards). But this will need very careful designing; as want to make sure that thread A isn't "starving" B by holding that lock for too long.
Looking at your code again, more closely ... maybe you could try to make your minPriority to be an AtomicInteger; the question is how that would relate to the other thread that is working the "size" of your queue.

How to make other threads wait for a given task result

I have a heavy operation with a server, lets call it String getData(), I want always to get an updated version of this data, so I do not apply a cache to the call.
My goal now is to avoid more than one getData call running at the same time. Making all the calls done after the first call (but before the first request end) to wait for the first result.
Example:
Thread 1 Thread 2 Thread 3
getData()
getData()[waiting]
getData()[waiting]
result1 received return result1 return result1
getData()
result2 received
return result2
how can I achieve that?
My rather inelegant idea is to store a Future when the first call comes in, and return this same future to other calls received while the first call is still pending. Then, when the first call completes, discard this Future, and create a new one when the next request comes in:
class OneAtATime<T> {
private final ExecutorService executor = Executors.newFixedThreadPool(1);
private final Supplier<T> supplier;
private Future<T> future;
OneAtATime(Supplier<T> supplier) {
this.supplier = supplier;
}
synchronized Future<T> submit() {
if (future == null) {
future = CompletableFuture.supplyAsync(supplier, executor);
future.thenRunAsync(() -> {
synchronized (JustOneExecutor.this) {
future = null;
}
}, executor);
}
return future;
}
}
An easy solution that does not involve any extra thread is to use ConcurrentHashMap#computeIfAbsent:
private final ConcurrentHashMap<String, String> instance =
new ConcurrentHashMap<>(1);
private String getData() {
final AtomicBoolean computed = new AtomicBoolean(false);
String data = instance.computeIfAbsent("KEY", () -> {
String data = internalGetData();
computed.set(true);
return data;
});
if(computed.get()) {
instance.clear();
}
return data;
}
private String internalGetData() {
// ...
}
You are describing the functionality of a BlockingQueue.
BlockingQueue<Data> dataQueue = new ArrayBlockingQueue(1);
Now all you need to do is dataQueue.take() and only one thread will get it's own data.

Thread issue while using Executor service

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.

Categories

Resources