Threads can be used to run more than one task at a time, you can keep one task from interfering with another task’s resources by using a lock (mutex) to synchronize the behavior of the two tasks. That is, if two tasks are stepping on each other over a shared resource (usually memory), you use a mutex to allow only one task at a time to access that resource.
In this article we will make tasks cooperate with each other, so that multiple tasks can work together to solve a problem. Now the issue is not about interfering with one another, but rather about working in unison, since portions of such problems must be solved before other portions can be solved.The key issue when tasks are cooperating is handshaking between those tasks.
We can use any of below methods for thread communication.
(1) Easiest, reliable, but inefficient way is to let thread-A periodically wake up and test a condition flag that thread-B will update. Sample code as below,
public class MyRunnable implements Runnable{
private volatile boolean flag;
@Override
public void run (){
while (!flag) { // condition Flag is a volatile varable
try {
Thread.sleep(1000);
}catch(InterruptedException e){
//Handle the exception
}
}
// do something
}
}
2. Thread-A Wait and Thread-B Notify
Similar to method 1 but use build-in Object wait and notify mechanism instead of thread sleep. More efficient than method 1 but easy to mess up (avoid to use).
Every object has an intrinsic lock or monitor lock; the lock is automatically acquired when enter synchronized block and release when exit the block.- thread has to have the (lockObject) lock to call wait
- the lock is release after calling wait (the thread suspended)
- the thread wakes up if the lockobject is interrupted or notify is called
public void run() {
synchronized (lockObject) { // has to be synchronized
while (!conditionFlag) { // volatile flag
try { lockObject.wait()} // select lock object
catch(InterruptedException e){}
}
}
}
In Thread B, call: lockObject.notifyAll() will trigger the event and wake up Thread A. (notifyAll is better than notify).
3. Thread-A Calls Thread-B Join
Thread A = new Thread(new Runnable() {
public void run() {
B.join(); // A will wait for Thread B to finish
// do something
}
}
Note join() is a method in Thread class, while wait() and notify() are in Object class.
4.Use Concurrency Utility Lock
It’s available from Java 1.5. It’s straight forward:
final Lock lock = new ReentrantLock();
...
lock.lock(); // block and wait until the lock is ready
try {
// do something
}
finally {
lock.unlock();
}
5.Use CountDownLatch
Available from Java 1.5 onwards.
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.
A CountDownLatch is a versatile synchronization tool and can be used for a number of purposes. A CountDownLatch initialized with a count of one serves as a simple on/off latch, or gate: all threads invoking await wait at the gate until it is opened by a thread invoking countDown(). A CountDownLatch initialized to N can be used to make one thread wait until N threads have completed some action, or some action has been completed N times.
6. Semaphores
Semaphore is a word coined from ancient Greek words (sêma, phoros) meaning ‘sign, bearing/bearer’. Semaphore is a technique used to control access to common resource for competeing multiple processes. Semaphore maintains a counter which keeps track of the number of resources available. When a process requests access to resource, semaphore checks the variable count and if it is less than total count then grants access and subsequently reduces the available count.
final Semaphore sem = new Semaphore(2); // binary semaphore - two permit
...
public void run() {
sem.acquire(); // wait until the semaphore is available
// do something
sem.release();
}
Thank you, hope you have enjoyed reading this article.