Concurrency Issue with HashMap and ReentrantLock - java

I have a piece of code that on startup creates a HashMap of key to ReentrantLock.
void constructor() {
this.lockMap = new HashMap<>();
for (int i=0; i<100; i++) {
this.lockMap.put(i, new ReentrantLock(true));
}
}
During concurrent execution, I try to lock the lock inside the lockMap in the following manner:
runConcurrently() {
ii = 10;
if (!lockMap.containsKey(ii)) {
log.error("lock id is not found in the lockMap " + ii);
}
locked = lockMap.get(ii).tryLock();
if (!locked) {
return;
}
runCriticialSection();
lockMap.get(ii).unlock();
}
void runCriticialSection() {
log.info("hello");
log.info("I'm here");
}
so here is what I have seen happen once in while every 4 hours the code is running, in a very rare occurrence.
I see these logs:
hello.
hello.
I'm here.
I'm here.
and then I see this log right after on third time accessing the hasmap on the same key ii =10:
lock id is not found in the map 10.
NullPointerExeception ... trying to access the map.
where I should see in guaranteed ordering:
hello.
I'm here.
hello.
I'm here.
The Hashmap never gets modified during execution at all.
is there an issue with hashmap not being concurrent hashmap? is get, not threadsafe in absence of modifications? I am specifically not using it due to locking slowness in concurrent hasmap. But the hashmap is only created on startup and never modified after. I find it very weird where it seems the lock has been acquired twice and it seems like the element is missing from the map.

There is no concurrency issue with the map itself, if the map is never modified after the constructor. If so, threads will only ever see that final version of the map. Else, the behaviour is undefined.
No exclusive access of the critical section
From your output, it appears that (at least) two threads accessed runCriticialSection simultaneously.
This is due to the fact that you are using a different lock for each value of ii. A lock only excludes another thread from locking it, if that other threads uses that same lock! Thus, threads that do not use the same value of ii, will effortlessly run runCriticialSection simultaneously. That can result in the described output anomaly as shown above, as follows:
Thread 1 executes log.info("hello");
Thread 2 executes log.info("hello");
Thread 1 executes log.info("I'm here");
Thread 2 executes log.info("I'm here");
If you want exclusive access to a section, always use the same lock surrounding that section.
Coding problems
When the check fails that ii maps to a lock, you should not continue but instead return or throw an exception. If you don't, locked = lockMap.get(ii).tryLock(); throws a NullPointerExcetpion, because lockMap.get(ii) returns null.
Between locking the lock and unlocking it, you are running user code, in the form of runCriticalSection. If you change the implementation of that method later and it starts throwing things: your lock will never unlock! Always use try ... finally with a lock.
Fixing these issues, could lead to the following code:
if (!lockMap.containsKey(ii)) {
log.error("lock id is not found in the lockMap " + ii);
return;
}
locked = lockMap.get(ii).tryLock();
if (!locked) {
return;
}
try {
runCriticialSection();
}
finally {
lockMap.get(ii).unlock();
}
Actually, I would just put the lock in a local variable, but that is a matter of opinion.
ReentrantLock lock = lockMap.get(ii);
if (lock == null) {
log.error("lock id is not found in the lockMap " + ii);
return;
}
locked = lock.tryLock();
if (!locked) {
return;
}
try {
runCriticialSection();
}
finally {
lock.unlock();
}

Related

How can I block ConcurrentHashMap get() operations during a put()

ConcurrentHashMap<String, Config> configStore = new ConcurrentHashMap<>();
...
void updateStore() {
Config newConfig = generateNewConfig();
Config oldConfig = configStore.get(configName);
if (newConfig.replaces(oldConfig)) {
configStore.put(configName, newConfig);
}
}
The ConcurrentHashMap can be read by multiple threads but can be updated only by a single thread. I'd like to block the get() operations when a put() operation is in progress. The rationale here being that if a put() operation is in progress, that implies the current entry in the map is stale and all get() operations should block until the put() is complete. How can I go about achieving this in Java without synchronizing the whole map?
It surely looks like you can defer this to compute and it will take care for that for you:
Config newConfig = generateNewConfig();
configStore.compute(
newConfig,
(oldConfig, value) -> {
if (newConfig.replaces(oldConfig)) {
return key;
}
return oldConfig;
}
);
You get two guarantees from using this method:
Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple
and
The entire method invocation is performed atomically
according to its documentation.
The accepted answer proposed to use compute(...) instead of put().
But if you want
to block the get() operations when a put() operation is in progress
then you should also use compute(...) instead of get().
That's because for ConcurrentHashMap get() doesn't block while compute() is in progress.
Here is a unit test to prove it:
#Test
public void myTest() throws Exception {
var map = new ConcurrentHashMap<>(Map.of("key", "v1"));
var insideComputeLatch = new CountDownLatch(1);
var threadGet = new Thread(() -> {
try {
insideComputeLatch.await();
System.out.println("threadGet: before get()");
var v = map.get("key");
System.out.println("threadGet: after get() (v='" + v + "')");
} catch (InterruptedException e) {
throw new Error(e);
}
});
var threadCompute = new Thread(() -> {
System.out.println("threadCompute: before compute()");
map.compute("key", (k, v) -> {
try {
System.out.println("threadCompute: inside compute(): start");
insideComputeLatch.countDown();
threadGet.join();
System.out.println("threadCompute: inside compute(): end");
return "v2";
} catch (InterruptedException e) {
throw new Error(e);
}
});
System.out.println("threadCompute: after compute()");
});
threadGet.start();
threadCompute.start();
threadGet.join();
threadCompute.join();
}
Output:
threadCompute: before compute()
threadCompute: inside compute(): start
threadGet: before get()
threadGet: after get() (v='v1')
threadCompute: inside compute(): end
threadCompute: after compute()
This fundamentally doesn't work. Think about it: When the code realizes that the information is stale, some time passes and then a .put call is done. Even if the .put call somehow blocks, the timeline is as follows:
Some event occurs in the cosmos that makes your config stale.
Some time passes. [A]
Your run some code that realizes that this is the case.
Some time passes. [B]
Your code begins the .put call.
An extremely tiny amount of time passes. [C]
Your code finishes the .put call.
What you're asking for is a strategy that eliminates [C] while doing absolutely nothing whatsoever to prevent reads of stale data at point [A] and [B], both of which seem considerably more problematic.
Whatever, just give me the answer
ConcurrentHashMap is just wrong if you want this, it's a thing that is designed for multiple concurrent (hence the name) accesses. What you want is a plain old HashMap, where every access to it goes through a lock. Or, you can turn the logic around: The only way to do what you want is to engage a lock for everything (both reads and writes); at which point the 'Concurrent' part of ConcurrentHashMap has become completely pointless:
private final Object lock = new Object[0];
public void updateConfig() {
synchronized (lock) {
// do the stuff
}
}
public Config getConfig(String key) {
synchronized (lock) {
return configStore.get(key);
}
}
NB: Use private locks; public locks are like public fields. If there is an object that code outside of your control can get a ref to, and you lock on it, you need to describe the behaviour of your code in regards to that lock, and then sign up to maintain that behaviour forever, or indicate clearly when you change the behaviour that your API just went through a breaking change, and you should thus also bump the major version number.
For the same reason public fields are almost invariably a bad idea in light of the fact that you want API control, you want the refs you lock on to be not accessible to anything except code you have under your direct control. Hence why the above code does not use the synchronized keyword on the method itself (as this is usually a ref that leaks all over the place).
Okay, maybe I want the different answer
The answer is either 'it does not matter' or 'use locks'. If [C] truly is all you care about, that time is so short, and pales in comparison to the times for [A] and [B], that if A/B are acceptable, certainly so is C. In that case: Just accept the situation.
Alternatively, you can use locks but lock even before the data ever becomes stale. This timeline guarantees that no stale data reads can ever occur:
The cosmos cannot ever make your data stale.
Your code, itself, is the only causal agent for stale date.
Whenever code runs that will or may end up making data stale:
Acquire a lock before you even start.
Do the thing that (may) make some config stale.
Keep holding on to the lock; fix the config.
Release the lock.
How can I go about achieving this in Java without synchronizing the whole map?
There are some good answers here but there is a simpler answer to use the ConcurrentMap.replace(key, oldValue, newValue) method which is atomic.
while (true) {
Config newConfig = generateNewConfig();
Config oldConfig = configStore.get(configName);
if (!newConfig.replaces(oldConfig)) {
// nothing to do
break;
}
// this is atomic and will only replace the config if the old hasn't changed
if (configStore.replace(configName, oldConfig, newConfig)) {
// if we replaced it then we are done
break;
}
// otherwise, loop around and create a new config
}

How to manually control which Thread enters critical region using Java Swing?

I am trying to create a simple Java Swing-based application that manually controls two threads which are both trying to continually increment an integer value. The application should be able to 'Start' and 'Stop' either of the threads (both threads incrementing the value simultaneously) and put either of the threads in the critical region (only one thread allowed to increment value).
Here's a screenshot of what I have, so that you may better understand what I am aiming for:
https://i.imgur.com/sQueUD7.png
I've created an "Incrementor" class which does the job of incrementing the int value, but if I try adding the synchronized keyword to the increment() method, I do not get the result I want.
private void increment() {
while (Thread.currentThread().isAlive()) {
if (Thread.currentThread().getName().equals("Thread 1")) {
if (t1Stop.isEnabled()) {
value++;
t1TextField.setText("Thread 1 has incremented value by 1. Current value = " + value + "\n");
}
} else if (Thread.currentThread().getName().equals("Thread 2")) {
if (t2Stop.isEnabled()) {
value++;
t2TextField.setText("Thread 2 has incremented value by 1. Current value = " + value + "\n");
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
Any advice on how to proceed?
I hope I've made it clear what it is I am looking for, if not, let me know and I'll update this post.
your problem is the dreaded thread lock !!
but if I try adding the synchronized keyword to the increment() method, I do not get the result I want.
of course ! Thread manager changes the "Working" thread whenever he feels like it !, and you should post more code here , but from the first look , you are running the same method in both threads , so it will be dropped down to 2 case :-
the good case !
the Thread Manager changes the thread after it finishes calling the increment method(good old win win for both threads ^-^).
the bad case (and this is what you have faced)
imagine that a thread accessed the method and before completing the method the thread managers changes it and when the other method tries to access it find's a big nasty synchronized in it's face with the lock in the other thread !from here is their is no guarantee what will happen but i can assure you that 90% of this cases result's only pleases the thread manager .
The application should be able to 'Start' and 'Stop' either of the threads (both threads incrementing the value simultaneously) and put either of the threads in the critical region (only one thread allowed to increment value).
sorry to break it to you but the thread manager is not-controllable my friend .
but we can suggest a fair amount of thing's to the thread manager , so what you are trying to achieve is not possible at the java thread manager .
and stopping thread's ooky dooky , but starting a thread after stopping it is big NO !!!
from the Thread.start() documentation
It is never legal to start a thread more than once.
In particular, a thread may not be restarted once it has completed
execution.
throws IllegalThreadStateException if the thread was already
started.
here's a very rich link were you can get the topic explained more widely at the oracle's
You can use object-level lock using synchronized keyword.
=> Object-level lock : To synchronize a non static method or block so that it can be accessed by only one thread at a time for that instance. It is used to protect non static data.
Example :
public class ClasswithCriticalSections {
private AtomicInteger count = new AtomicInteger(0);
public synchronized int increment() {
count.incrementAndGet();
return count;
}
}
or
public class ClasswithCriticalSections {
Object lock1 = new Object();
Object lock2 = new Object();
private AtomicInteger count = new AtomicInteger(0);
public int increment() {
synchronized(lock1) {
count.incrementAndGet();
return count;
}
}
public int decrement() {
synchronized(lock2) {
count.addAndGet(-1);
return count;
}
}
}

Can dead locks be prevented by always using tryLock on java Lock?

Deadlocks only seem possible if there is a cyclic dependency created by the possibility of one or more threads creating a loop through lockable resources.
One option is to avoid these cycles through careful static analysis or through a design pattern for acquiring locks.
However can we prevent deadlocks by using tryLock on the Lock interface?
tryLock attemps to get the lock atomically, and returns true if successful, if its already locked then it returns false so we can simply skip over the code.
int sharedStateA = 0;
int sharedStateB = 0;
Lock lockA = new ReentrantLock();
Lock lockB = new ReentrantLock();
// possible deadlock safe solution
// executed by thread 1
void deadLockSafeUpdateAthenB(){
try {
if (lockA.tryLock()){
sharedStateA = sharedStateA + 1;
try {
if (lockB.tryLock()){
sharedStateB = sharedStateB + 1;
}
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
// executed by thread 2
void deadLockSafeUpdateBthenA(){
try {
if (lockB.tryLock()){
sharedStateB = sharedStateB + 1;
try {
if (lockA.tryLock()){
sharedStateA = sharedStateA + 1;
}
} finally {
lockA.unlock();
}
}
} finally {
lockB.unlock();
}
}
Your code with Lock.tryLock() is deadlock safe but you should try to use the other method,
public boolean tryLock(long timeout,
TimeUnit unit)
if your threads have short run times. The call - tryLock(0,TimeUnit.SECONDS) is better than Lock.tryLock() because it honors fairness policy i.e. lock waiting queue is honored while tryLock() doesn't honor that.
Even if a static analysis tells us that a code is deadlock prone but its not always necessary that a deadlock prone code will actually produce deadlocks since its all an unlucky timing game so your target with tryLock() should be to produce functionally the same program as with deadlock prone code assuming that deadlock doesn't occur.
Fixing one problem shouldn't introduce other issues and in your code it looks quite possible that at some unlucky timing, one thread might not execute at all so I suggest to use timed trylock instead of barging trylock if its mandatory for lock acquisition to be in that order.
Hope it helps !!

Memory inconsistency with synchronized mutex

Update:
When I first posted this, I was fairly certain the code was broken. Now, I'm no longer sure about what I'd observed. The biggest problem I'm having is that I can't seem to apply 17.4. Memory Model and state straight out whether it should or shouldn't work.
This following code is broken.
It's overly complex for what it's trying to achieve, but furthermore, it's thread-unsafe in that I've observed that it can indefinitely wait at c. I'm not worried about the former (one could use a ReentrantLock or CountDownLatch for sounder code), but I'm wondering, what's the reason for the latter?
static final ConcurrentHashMap<Integer, Object> mutex = new ConcurrentHashMap<>();
public static brokenFoo() {
Object ourLock = new Object();
for (;;) {
Object theirLock = mutex.putIfAbsent(0, ourLock);
if (theirLock == null) {
break;
}
synchronized (theirLock) { // a
if (mutex.get(0) != theirLock) { // b
continue;
}
theirLock.wait(); // c
} // d
}
try {
// critical section
} finally {
synchronized (ourLock) { // e
mutex.remove(0); // f
ourLock.notifyAll(); // g
} // h
}
}
I've thought in terms of happens-befores:
hb(f, h) and hb(h, a) therefore hb(f, a)
hb(c, d) and hb(d, e) therefore hb(c, e)
But, this doesn't seem to prove or disprove anything.
Edit: (Above question fails to really explain what this code should do.)
Expected:
brokenFoo() is called by multiple threads and the above code is supposed to provide mutual exclusion over // critical section.
If two or more threads enter brokenFoo() at the same time, only one should proceed to // critical section, while others wait somewhere prior.
After the thread in // critical section has exited, another should proceed to take its place.
Actual:
It's been observed that there're threads that are waiting at c even though no other threads are in brokenFoo().
It might be the case that one thread calls notifyAll() before another thread starts to wait(). This may happen due to a spurious wakeup:
thread 1 enters the critical section
thread 2 starts to wait()
a spurious wakeup occurs in thread 2
thread 1 enters the synchronized block and notifies on the lock
thread 2 enters the synchronized block and waits indefinitely
Or thread 1 just happened to execute before thread 2. While your code is correct in terms of the JMM, its liveness is not guaranteed. That's why you should use a CountDownLatch instead of the notify/wait mechanism.

Need advice on synchronization of Java Vector / ConcurrentModificationException

In a legacy application I have a Vector that keeps a chronological list of files to process and multiple threads ask it for the next file to process. (Note that I realize that there are likely better collections to use (feel free to suggest), but I don't have time for a change of that magnitude right now.)
At a scheduled interval, another thread checks the working directory to see if any files appear to have been orphaned because something went wrong. The method called by this thread occasionally throws a ConcurrentModificationException if the system is abnormally busy. So I know that at least two threads are trying to use the Vector at once.
Here is the code. I believe the issue is the use of the clone() on the returned Vector.
private synchronized boolean isFileInDataStore( File fileToCheck ){
boolean inFile = false;
for( File wf : (Vector<File>)m_dataStore.getFileList().clone() ){
File zipName = new File( Tools.replaceFileExtension(fileToCheck.getAbsolutePath(), ZIP_EXTENSION) );
if(wf.getAbsolutePath().equals(zipName.getAbsolutePath()) ){
inFile = true;
break;
}
}
return inFile;
}
The getFileList() method is as follows:
public synchronized Vector<File> getFileList() {
synchronized(fileList){
return fileList;
}
}
As a quick fix, would changing the getFileList method to return a copy of the vector as follows suffice?
public synchronized Vector<File> getFileListCopy() {
synchronized(fileList){
return (Vector<File>)fileList.clone();
}
}
I must admit that I am generally confused by the use of synchronized in Java as it pertains to collections, as simply declaring the method as such is not enough. As a bonus question, is declaring the method as synchronized and wrapping the return call with another synchronized block just crazy coding? Looks redundant.
EDIT: Here are the other methods which touch the list.
public synchronized boolean addFile(File aFile) {
boolean added = false;
synchronized(fileList){
if( !fileList.contains(aFile) ){
added = fileList.add(aFile);
}
}
notifyAll();
return added;
}
public synchronized void removeFile( File dirToImport, File aFile ) {
if(aFile!=null){
synchronized(fileList){
fileList.remove(aFile);
}
// Create a dummy list so I can synchronize it.
List<File> zipFiles = new ArrayList<File>();
synchronized(zipFiles){
// Populate with actual list
zipFiles = (List<File>)diodeTable.get(dirToImport);
if(zipFiles!=null){
zipFiles.remove(aFile);
// Repopulate list if the number falls below the number of importer threads.
if( zipFiles.size()<importerThreadCount ){
diodeTable.put(dirToImport, getFileList( dirToImport ));
}
}
}
notifyAll();
}
}
Basically, there are two separate issues here: sycnhronization and ConcurrentModificationException. Vector in contrast to e.g. ArrayList is synchronized internally so basic operation like add() or get() do not need synchronization. But you can get ConcurrentModificationException even from a single thread if you are iterating over a Vector and modify it in the meantime, e.g. by inserting an element. So, if you performed a modifying operation inside your for loop, you could break the Vector even with a single thread. Now, if you return your Vector outside of your class, you don't prevent anyone from modifyuing it without proper synchronization in their code. Synchronization on fileList in the original version of getFileList() is pointless. Returning a copy instead of original could help, as could using a collection which allows modification while iterating, like CopyOnWriteArrayList (but do note the additional cost of modifications, it may be a showstopper in some cases).
"I am generally confused by the use of synchronized in Java as it
pertains to collections, as simply declaring the method as such is not
enough"
Correct. synchronized on a method means that only one thread at a time may enter the method. But if the same collection is visible from multiple methods, then this doesn't help much.
To prevent two threads accessing the same collection at the same time, they need to synchronize on the same object - e.g. the collection itself. You have done this in some of your methods, but isFileInDataStore appears to access a collection returned by getFileList without synchronizing on it.
Note that obtaining the collection in a synchronized manner, as you have done in getFileList, isn't enough - it's the accessing that needs synchronizing. Cloning the collection would (probably) fix the issue if you only need read-access.
As well as looking at synchronizing, I suggest you track down which threads are involved - e.g. print out the call stack of the exception and/or use a debugger. It's better to really understand what's going on than to just synchronize and clone until the errors go away!
Where does the m_dataStore get updated? That's a likely culprit if it's not synchronized.
First, you should move your logic to whatever class is m_dataStore if you haven't.
Once you've done that, make your list final, and synchronize on it ONLY if you are modifying its elements. Threads that only need to read it, don't need synchronized access. They may end up polling an outdated list, but I suppose that is not a problem. This gets you increased performance.
As far as I can tell, you would only need to synchronize when adding and removing, and only need to lock your list.
e.g.
package answer;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Example {
public static void main(String[] args)
{
Example c = new Example();
c.runit();
}
public void runit()
{
Thread.currentThread().setName("Thread-1");
new Thread("Thread-2")
{
#Override
public void run() {
test1(true);
}
}.start();
// Force a scenario where Thread-1 allows Thread-2 to acquire the lock
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
}
// At this point, Thread-2 has acquired the lock, but it has entered its wait() method, releasing the lock
test1(false);
}
public synchronized void test1(boolean wait)
{
System.out.println( Thread.currentThread().getName() + " : Starting...");
try {
if (wait)
{
// Apparently the current thread is supposed to wait for some other thread to do something...
wait();
} else {
// The current thread is supposed to keep running with the lock
doSomeWorkThatRequiresALockLikeRemoveOrAdd();
System.out.println( Thread.currentThread().getName() + " : Our work is done. About to wake up the other thread(s) in 2s...");
Thread.sleep(2000);
// Tell Thread-2 that it we have done our work and that they don't have to spare the CPU anymore.
// This essentially tells it "hey don't wait anymore, start checking if you can get the lock"
// Try commenting this line and you will see that Thread-2 never wakes up...
notifyAll();
// This should show you that Thread-1 will still have the lock at this point (even after calling notifyAll).
//Thread-2 will not print "after wait/notify" for as long as Thread-1 is running this method. The lock is still owned by Thread-1.
Thread.sleep(1000);
}
System.out.println( Thread.currentThread().getName() + " : after wait/notify");
} catch (InterruptedException ex) {
Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void doSomeWorkThatRequiresALockLikeRemoveOrAdd()
{
// Do some work that requires a lock like remove or add
}
}

Categories