Java concurrency - avoid busy wait using wait() and notify() inter-thread communication methods
In this article, we would like to show to to avoid busy wait using wait()
and notify()
inter-thread communication methods in Java.
Note: the preseted approach is not the best choice to synchronize threads, but in this post we use it for learning purpose.
Quick solution:
// shared objects:
int counter = 0; // e.g. as class field
Object lock = new Object(); // e.g. as class field
// thread 1:
Thread thread1 = new Thread(() -> {
// ...
// do task 1
// ...
synchronized(lock) {
counter++;
lock.notify();
}
});
// thread 2:
Thread thread2 = new Thread(() -> {
// ...
// do task 2
// ...
synchronized(lock) {
counter++;
lock.notify();
}
});
// e.g. in main thread - runs tasks and waits until are ended:
synchronized(lock) {
thread1.start();
thread2.start();
while(counter < 2) {
lock.wait();
}
}
Practical example
The main idea of the presented solution is to use synchronized
keyword to control and exchange tasks state between threads. It prevents against same-time critical sections execution by different threads. counter
object helps to detect when all tasks are done. The main thread waits for notification from other threads when some task is done - when notify()
is called, wait()
method is unblocekd once.
Warning:
It is not good idea to use
wait()
andnotify()
methods, because there is always risk thenotify()
will be executed beforewait()
leaving program blocked.
Source code:
public class Program {
public static void main(String[] args) throws InterruptedException {
// shared objects:
final Object lock = new Object();
final Counter counter = new Counter(); // we use Couter class to control state from lambdas
// [Thread: Thread-0] - Task 1
Thread thread1 = new Thread(() -> {
printMessage("task 1 started");
try {
simulateTask(300); // longer task
} finally {
printMessage("task 1 done");
synchronized (lock) {
counter.increment();
lock.notify();
}
}
});
// [Thread: Thread-1] - Task 2
Thread thread2 = new Thread(() -> {
printMessage("task 2 started");
try {
simulateTask(100); // shorter task
} finally {
printMessage("task 2 done");
synchronized (lock) {
counter.increment();
lock.notify();
}
}
});
// [Thread: main]
synchronized (lock) {
counter.reset();
thread1.start();
thread2.start();
printMessage("tasks started");
while (counter.get() < 2) {
lock.wait();
printMessage("thread notified");
}
printMessage("tasks done");
}
}
// helper methods
private static void simulateTask(int timeout) {
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void printMessage(String message) {
Thread thread = Thread.currentThread();
System.out.println("[" + thread.getName() + "]: " + message);
}
// helper classes
private static class Counter {
private int value = 0;
private int get() {
return this.value;
}
private void increment() {
this.value += 1;
}
private void reset() {
this.value = 0;
}
}
}
Output:
[main]: tasks started
[Thread-1]: task 2 started
[Thread-0]: task 1 started
[Thread-1]: task 2 done
[main]: thread notified
[Thread-0]: task 1 done
[main]: thread notified
[main]: tasks done