I have many threads which monitor a certain state. If the application gets into that state, then I need to do some extra work. I want to allow just 1 thread to execute that and want to block the others until that work is finished. Blocking mean, that they must not execute that task again.
I have the following scenario:
ReentrantLock lock = new ReentrantLock
void doSomething() {
if (lock.tryLock()) {
try {
doSomeWork()
} finally {
lock.unLock()
}
} else {
// wait for DoSomeWork is done
}
}
I can monitor lock.isLocked() in a loop, but actually I just want to have some sort of wait until the work is finished by the other thread.
According to documentation about ReentrantLock class:
It is recommended practice to always immediately follow a call to
lock with a try block, most typically in a before/after construction
such as:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
From the documentation for lock() method:"If the lock is held by another thread then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired" and this is what you want to be guaranteed in your scenario.
Related
I am working on understanding deadlock basics so I came up with below code. I have two threads acquiring locks in opposite order but they're not deadlocking. When I run it I see all the printouts. What am I doing wrong?
public class DeadlockBasics {
private Lock lockA = new ReentrantLock();
private Lock lockB = new ReentrantLock();
public static void main(String[] args) {
DeadlockBasics dk = new DeadlockBasics();
dk.execute();
}
private void execute() {
new Thread(this::processThis).start();
new Thread(this::processThat).start();
}
// called by thread 1
public void processThis() {
lockA.lock();
// process resource A
System.out.println("resource A -Thread1");
lockB.lock();
// process resource B
System.out.println("resource B -Thread1");
lockA.unlock();
lockB.unlock();
}
// called by thread 2
public void processThat() {
lockB.lock();
// process resource B
System.out.println("resource B -Thread2");
lockA.lock();
// process resource A
System.out.println("resource A -Thread2");
lockA.unlock();
lockB.unlock();
}
}
First of all there is no garantee which threads is start first. To get the deadlock one of the thread has to take a lock on lockA and then the second thread has to take a lock on lockB or visa versa.
public void processThis() {
lockA.lock();
// here the control should be switched to another thread
System.out.println("resource A -Thread1");
lockB.lock();
...
But there may not be enough time to switch between thread because you have just a few lines of code.. It is too fast. To emulate some long work add delay before the second lock to both methods
lockA.lock();
Thread.sleep(200); // 200 milis
Then the second thread will be able to lock lockB before the first release both of them
This could indeed result in a deadlock but not always, for example if the processThis() is completely executed and then the processThat() or vice versa there is no deadlock. You can try to add a Thread.delay(100) or a Thread.yield() to steer the threads execution towards the deadlock or even removing the unlocks to a certain deadlock.
Your code is a good example of dead lock, since ReenttrantLock is a mutual exclusion lock with same behavior as the implicit monitor lock access by using synchronized. However you don't see the deadlock because of this part:
private void execute() {
new Thread(this::processThis).start();
new Thread(this::processThat).start();
}
After the first thread is created and started, it will takes a while to create the second thread. It takes the JVM about 50 us or maybe even less to create a new thread, it sounds very short, but it is enough for the first thread to be finished and therefore a dead lock will not happen.
I added a Thread.sleep(); into your code so the both threads could be executed somehow parallely.
package com.company;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockBasics {
private Lock lockA = new ReentrantLock();
private Lock lockB = new ReentrantLock();
public static void main(String[] args) {
DeadlockBasics dk = new DeadlockBasics();
dk.execute();
}
private void execute() {
new Thread(this::processThis).start();
new Thread(this::processThat).start();
}
// called by thread 1
private void processThis() {
lockA.lock();
// process resource A
try {
Thread.sleep(1000); //Wait for thread 2 to be executed
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1 will own lock a");
lockB.lock();
// process resource B
System.out.println("Thread 1 will own lock b");
lockA.unlock();
lockB.unlock();
// Both locks will now released from thread 1
}
// called by thread 2
private void processThat() {
lockB.lock();
// process resource B
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2 will own lock b");
lockA.lock();
// process resource A
System.out.println("Thread 2 will own lock a");
lockA.unlock();
lockB.unlock();
// Both locks are released by thread 2
}
}
Two points:
Release locks in the reverse order of acquiring them. That is, processThis should reverse the order of removing the locks. For your example, the order doesn't matter. But if processThis attempted to acquire a new lock on A before releasing the lock on B a deadlock could again occur. More generally, you'll find it easier to think about locks by considering their scope and by avoiding overlapping but non-enclosing scopes.
To better highlight the problem, I would put in call to wait after acquiring the first lock in each of threads, and have execute launch both threads then invoke notify on both threads.
The code below is my run method. This stat status does not take the newly updated status from my stop method.
#Override
public void run() {
synchronized (this) {
while (!stat) {
try {
this.wait();
} catch (InterruptedException ex) {
Logger.getLogger(TrafficLightSimulator.class.getName()).log(Level.SEVERE, null, ex);
} }
}
}
In the above code, the program does not enter while loop. It is because the stat boolean new changed value from the stop method is not taken in run method.
This is my new stop
public void stop() {
synchronized(this) {
this.stopStat = false;
this.notifyAll();
}
}
I even defined the stat as the volatile boolean variable. However, this also does not seem to work.
While you have the "waiting" part correct, your "setting" part is missing some important parts.
The first part that is missing is the lock:
public void stop() {
synchronized(this) {
this.stat = true;
}
}
This lock makes sure that only 1 thread can change/access it at the same time, as is required by the Java memory model. Without this lock (and without volatile), Java makes no guarantee that changes to this variable are seen by other threads.
The next part that missing is the notifying part, it is important to "wake" up all waiting threads when the condition is changed:
public void stop() {
synchronized(this) {
this.stat = true;
this.notifyAll();
}
}
The last part of your error happens due the fact you are setting the variable to true, while for the code to be inside the while loop, the variable is already true. You probably want to set it to false instead
public void stop() {
synchronized(this) {
this.stat = false;
this.notifyAll();
}
}
Memory visibility isn't an issue here because both the code reading the flag and the code setting the flag are synchronized, holding the same lock. If your code won't enter the while loop it must be that some other thread has the lock.
This is another reason (in addition to the reasons below) to use interrupt instead of wait/notify for this. Interruption doesn't depend on acquiring a lock to work.
Use interrupt for this instead. There's no good reason for your own flag here when one is provided for you, using wait/notify for this is unnecessary and can cause problems since you may need to wait/notify for other reasons.
The Java api docs advise against locking on threads, by the way. That's what join does. Implicit locking doesn't have a way to have different conditions get separate notifications for a lock (the way ReentrantLock does).
For a thread that does things with intermittent sleeps in between, the run method can look like:
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
doStuff();
Thread.sleep(1000L);
} catch (InterruptedException e) {
// no need to log this as an error, it's not an error
Thread.currentThread().interrupt();
}
}
}
Real world code uses threadpools where you submit Runnable or Callable tasks, the java.util.concurrent classes expect your code to handle interruption.
Semaphore:
public void enter() throws InterruptedException {
synchronized (block) {
++current;
if (current > permits) {
try {
block.wait();
} catch (InterruptedException e) {
throw e;
}
}
}
}
public void realese() {
synchronized (block) {
--current;
block.notify();
}
}
How to make a queue in the semaphore? I want threads to be executed in the order of calling the enter().
Here's one way:
Have the release() method wake up every waiting thread (i.e., block.notifyAll())
In the enter() method, have each waiting thread look at the head of the queue.
If the thread at the head of the queue is the current thread, pop the queue and complete the enter() call, -OR-
Go back to waiting otherwise.
But there's a subtle problem that I'll leave to you to work out how to fix:
If thread A wakes up, finds itself at the head of the queue, pops the queue and returns,
Then, thread B could wakes up, finds its self at the head of the queue, and it also pops the queue and returns.
And there you have one release() call which just allowed two threads to enter().
That would be bad. So, you'll need to figure out a way to guarantee that no more than one thread can complete the enter() call each time release() is called.
If your using a newer java you can just add a fairness setting to the constructor.
Semaphore(int permits, boolean fair)
like this
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
Class clazz has two methods methodA() and methodB().
How to ensure that methodB is "blocked" if some threads are in methodA in Java (I am using Java 8)?
By "blocking methodB", I mean that "wait until no threads are in methodA()". (Thanks to #AndyTurner)
Note that the requirement above allows the following situations:
Multiple threads are simultaneously in methodA.
Multiple threads are in methodB while no threads are in methodA.
Threads in methodB does not prevent other threads from entering methodA.
My trial: I use StampedLock lock = new StampedLock.
In methodA, call long stamp = lock.readLock()
Create a new method unlockB and call lock.unlockRead(stamp) in it.
In methodB, call long stamp = lock.writeLock() and lock.unlockWrite(stamp).
However, this locking strategy disallows the second and the third situations above.
Edit: I realize that I have not clearly specified the requirements of the synchronization between methodA and methodB. The approach given by #JaroslawPawlak works for the current requirement (I accept it), but not for my original intention (maybe I should first clarify it and then post it in another thread).
I think this can do the trick:
private final Lock lock = new ReentrantLock();
private final Semaphore semaphore = new Semaphore(1);
private int threadsInA = 0;
public void methodA() {
lock.lock();
threadsInA++;
semaphore.tryAcquire();
lock.unlock();
// your code
lock.lock();
threadsInA--;
if (threadsInA == 0) {
semaphore.release();
}
lock.unlock();
}
public void methodB() throws InterruptedException {
semaphore.acquire();
semaphore.release();
// your code
}
Threads entering methodA increase the count and try to acquire a permit from semaphore (i.e. they take 1 permit if available, but if not available they just continue without a permit). When the last thread leaves methodA, the permit is returned. We cannot use AtomicInteger since changing the count and acquiring/releasing permit from semaphore must be atomic.
Threads entering methodB need to have a permit (and will wait for one if not available), but after they get it they return it immediately allowing others threads to enter methodB.
EDIT:
Another simpler version:
private final int MAX_THREADS = 1_000;
private final Semaphore semaphore = new Semaphore(MAX_THREADS);
public void methodA() throws InterruptedException {
semaphore.acquire();
// your code
semaphore.release();
}
public void methodB() throws InterruptedException {
semaphore.acquire(MAX_THREADS);
semaphore.release(MAX_THREADS);
// your code
}
Every thread in methodA holds a single permit which is released when the thread leaves methodA.
Threads entering methodB wait until all 1000 permits are available (i.e. no threads in methodA), but don't hold them, which allows other threads to enter both methods while methodB is still being executed.
You can't really prevent that methodA or methodB is called (while other threads are inside the other method) but you can implement thread intercommunication in such a way so that you can still achieve what you want.
class MutualEx {
boolean lock = false;
public synchronized void methodA() {
if (lock) {
try {
wait();
}catch (InterruptedException e) {
}
}
//do some processing
lock = true;
notifyAll();
}
public synchronized void methodB() {
if (!lock) {
try {
wait();
}catch (InterruptedException e) {
}
}
//do some processing
lock = false;
notifyAll();
}
}
Now, for this to work any Thread object you create should have a reference to the same instance of MutualEx object.
Why not using an kind of external orchestrator?
I mean another class that will be responsible to call the methodA or methodB when it allowed.
Multi-thread can still be handle via locking or maybe just with some AtomicBoolean(s).
Please find below a naive draft of how to do it.
public class MyOrchestrator {
#Autowired
private ClassWithMethods classWithMethods;
private AtomicBoolean aBoolean = = new AtomicBoolean(true);
public Object callTheDesiredMethodIfPossible(Method method, Object... params) {
if(aBoolean.compareAndSet(true, false)) {
return method.invoke(classWithMethods, params);
aBoolean.set(true);
}
if ("methodA".equals(method.getName())) {
return method.invoke(classWithMethods, params);
}
}
}
In very simple terms what you all need is ENTER methodB only if no thread inside methodA.
Simply you can have a global counter, first initialized to 0 to record the number of threads that are currently inside methodA(). You should have a lock/mutex assigned to protect the variable count.
Threads entering methodsA do count++.
Threads exiting methodA do count-- .
Threads that are entering methodB first should check whether count == 0.
methodA(){
mutex.lock();
count++;
mutex.signal();
//do stuff
mutex.lock();
count--;
mutex.signal();
}
methodB(){
mutex.lock();
if(count != 0){
mutex.signal();
return;
}
mutex.signal();
//do stuff
}
You would need an int to count threads in methodA, and ReentrantLock.Condition to signal all threads waiting in methodB once there are no threads in methodA:
AtomicInteger threadsInMethodA = new AtomicInteger(0);
Lock threadsForMethodBLock = new ReentrantLock();
Condition signalWaitingThreadsForMethodB = threadsForMethodBLock.newCondition();
public void methodA() {
threadsInMethodA.incrementAndGet();
//do stuff
if (threadsInMethodA.decrementAndGet() == 0) {
try {
threadsForMethodBLock.lock();
signalWaitingThreadsForMethodB.signalAll();
} finally {
threadsForMethodBLock.unlock();
}
}
}
public void methodB() {
try {
threadsForMethodBLock.lock();
while (!Thread.isInterrupted() && threadsInMethodA.get() != 0) {
try {
signalWaitingThreadsForMethodB.await();
} catch (InterruptedException e) {
Thread.interrupt();
throw new RuntimeException("Not sure if you should continue doing stuff in case of interruption");
}
}
signalWaitingThreadsForMethodB.signalAll();
} finally {
threadsForMethodBLock.unlock();
}
//do stuff
}
So each thread entering methodB will first check if nobody in methodA, and signal previous waiting threads. On the other hand, each thread entering methodA will increment counter to prevent new threads doing work in methodB, and on decrement it will release all the threads waiting to do stuff in methodB if no threads left inside methodA.
public class MyLockConditionTest {
private final Lock alock = new ReentrantLock();
private final Condition condition = alock.newCondition();
private String message = null;
public void waitForCallback() {
alock.lock();
try {
// wait for callback from a remote server
condition.await();
doSomething(message);
} finally {
alock.unlock();
}
}
// another thread will call this method as a callback to wake up the thread called condition.await()
public void onCallbackReceived(String message) {
alock.lock();
try {
this.message = message
condition.signal();
} finally {
alock.unlock();
}
}
}
I have this code using ReentrantLock and Condition to implement a class to wait on certain callbacks from a remote server. I tested this code and seems working but I have a few questions.
Why do I need to do alock.lock() / unlock() in onCallbackReceived(). Without calling lock() / unlock(), I was getting an IllegalState exception. I am confused because the lock is held by the caller of waitForCallback() when onCallbackReceived() invoked by another thread so alock.lock() in onCallbackReceived() will always fail.
Do I need to wrap condition.await() in waitForCallback() with while loop?
while(message==null)
condition.await();
Why do I need to do alock.lock() / unlock() in onCallbackReceived(). Without calling lock() / unlock(), I was getting an IllegalState exception.
You are trying to signal a condition in which you do not own the lock. The only way you would hold the lock is if onCallbackReceived is called from doSomething which there is no indication that is occurring.
Do I need to wrap condition.await() in waitForCallback() with while loop?
Yes, imagine you have 5 threads stopped on the condition and 5 threads blocked on lock. Only one thread can wake up. What if when a waiting thread finally wakes up another thread had null'd that field out? You will need to check again to ensure the predicate is still true.