Some of the content comes from the following Blogs:
https://www.cnblogs.com/skywang12345/p/3496716.html
1. Thread communication mode
For the communication mode between threads, we used Object.wait() and Object.notify() to synchronize with the synchronized keyword. The two can be used together to realize the communication between threads.
Later, it was found in the JUC concurrency package that Lock can be used to replace the synchronized keyword to realize the synchronization between threads, and the use of Lock has more powerful functions than the synchronized method. In order to cooperate with Lock and realize the communication between threads, Condition must be used.
The function of Condition is to control the lock more accurately. The await() method in Condition is equivalent to the wait() method of Object, the signal() method in Condition is equivalent to the notify() method of Object, and the signalAll() method in Condition is equivalent to the notifyAll() method of Object. The difference is that the wait(), notify(), and notifyAll() methods in the Object are used in combination with the synchronized keyword, while the Condition needs to be used in combination with the exclusive lock / shared lock.
1.1 advantages of using Condition
It can more finely control the sleep and wake-up of multithreading. For the same lock, we can create multiple conditions and use different conditions in different situations.
Ability to communicate between multiple threads. Object's wait(), notify() and notifyAll() methods can only realize the communication between two threads, while Lock object can create countless "conditions" through the newCondition() method. Through these conditions, we can successfully realize the data communication between multiple threads and control them.
1.2 Condition
Condition is an interface under the java.util.concurrent.locks package, which provides methods for thread communication.
public interface Condition { // Make the current thread join the waiting queue and release the lock. Wake up when it is notified and interrupted. void await() throws InterruptedException; // Similar to await(), this method is not sensitive to interrupts and is only awakened when notified. void awaitUninterruptibly(); // Similar to await(), this method returns false if it is not notified or interrupted within the specified time. boolean await(long time, TimeUnit unit) throws InterruptedException; // The current thread enters a waiting state and is awakened after being notified, interrupted or timed out. The return value represents the remaining time. The timeout return value is 0 or negative. long awaitNanos(long nanosTimeout) throws InterruptedException; // It is similar to awaitNanos(long nanosTimeout), except that the parameter becomes the specified date. boolean awaitUntil(Date deadline) throws InterruptedException; // Wake up a thread in the waiting queue. void signal(); // Wake up all threads in the waiting queue. void signalAll(); }
1.3 obtaining specific conditions on Lock
The Condition instance is essentially bound to a Lock. A Lock can have multiple conditions, that is, multiple waiting and notification. To get a Condition instance for a specific Lock instance, use Lock's newCondition() method.
newCondition() returns the Condition instance used with the current Lock instance.
Similar to the functions of Object.wait() and Object.notify(), Object.wait() and Object.notify() need to be used in combination with synchronized. Condition needs to be used in combination with ReentrantLock.
2 use Condition to realize thread communication
2.1 methods using synchronized and Object classes
The synchronized and Object classes are used to realize the alternate printing of two threads. The code is as follows:
public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(() -> {ticket.sale();}, "Thread 1").start(); new Thread(() -> {ticket.sale();}, "Thread 2").start(); } } class Ticket { private int num = 10; public void sale() { while (num > 0) { synchronized (Ticket.class) { Ticket.class.notify(); if (num > 0) { System.out.println(Thread.currentThread().getName() + " >>> " + num--); } if (num > 0) { try { Ticket.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
The operation results are as follows:
Thread 1 >>> 10 Thread 2 >>> 9 Thread 1 >>> 8 Thread 2 >>> 7 Thread 1 >>> 6 Thread 2 >>> 5 Thread 1 >>> 4 Thread 2 >>> 3 Thread 1 >>> 2 Thread 2 >>> 1
2.2 methods using Lock and Condition classes
Use the methods of Lock and Condition classes to realize alternate printing of two threads. The code is as follows:
public class Demo { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(() -> {ticket.sale();}, "Thread 1").start(); new Thread(() -> {ticket.sale();}, "Thread 2").start(); } } class Ticket { private int num = 10; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void sale() { while (num > 0) { lock.lock(); try { condition.signal(); if (num > 0) { System.out.println(Thread.currentThread().getName() + " >>> " + num--); } if (num > 0) { condition.await(); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
The operation results are as follows:
Thread 1 >>> 10 Thread 2 >>> 9 Thread 1 >>> 8 Thread 2 >>> 7 Thread 1 >>> 6 Thread 2 >>> 5 Thread 1 >>> 4 Thread 2 >>> 3 Thread 1 >>> 2 Thread 2 >>> 1
2.3 use the methods of Lock and Condition classes to realize the cyclic printing of three threads in sequence
Use the methods of Lock and Condition classes to print three threads in order. You need to wake up the specified thread. The code is as follows:
public class Demoo { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(() -> {ticket.sale1();}, "Thread 1").start(); new Thread(() -> {ticket.sale2();}, "Thread 2").start(); new Thread(() -> {ticket.sale3();}, "Thread 3").start(); } } class Ticket { private int num = 10; Lock lock = new ReentrantLock(); Condition c1 = lock.newCondition(); Condition c2 = lock.newCondition(); Condition c3 = lock.newCondition(); public void sale1() { lock.lock(); try { while (num > 0) { c2.signal(); System.out.println(Thread.currentThread().getName() + " >>> " + num--); c1.await(); if (num <= 0) { c3.signal(); } } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void sale2() { lock.lock(); try { while (num > 0) { c3.signal(); System.out.println(Thread.currentThread().getName() + " >>> " + num--); c2.await(); if (num <= 0) { c1.signal(); } } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void sale3() { lock.lock(); try { while (num > 0) { c1.signal(); System.out.println(Thread.currentThread().getName() + " >>> " + num--); c3.await(); if (num <= 0) { c2.signal(); } } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
The operation results are as follows:
Thread 1 >>> 10 Thread 2 >>> 9 Thread 3 >>> 8 Thread 1 >>> 7 Thread 2 >>> 6 Thread 3 >>> 5 Thread 1 >>> 4 Thread 2 >>> 3 Thread 3 >>> 2 Thread 1 >>> 1
2.4 differences between await(), signal(), signalAll() in condition and wait(), notify(), notifyAll() in Object
2.4.1 different ways of use
The await() method in Condition is equivalent to the wait() method of Object, the signal() method in Condition is equivalent to the notify() method of Object, and the signalAll() method in Condition is equivalent to the notifyAll() method of Object.
The difference is that these methods in Object are used in combination with synchronization lock, while Condition needs to be used in combination with mutex / shared lock.
2.4.2 more powerful functions
Condition is more powerful in that it can more finely control the sleep and wake-up of multithreads. For the same lock, you can create multiple conditions and use different conditions in different situations.
For example, suppose multiple threads read / write to the same buffer: wake up the "read thread" after writing data to the buffer. Wake up the "write thread" after reading data from the buffer. When the buffer is full, the "write thread" needs to wait. When the buffer is empty, the "read thread" needs to wait.
If you use wait(), notify(), and notifyAll() in the Object class to implement the buffer, when you need to wake up the "read thread" after writing data to the buffer, it is impossible to explicitly specify to wake up the "read thread" through notify() or notifyAll(), but you can only wake up all threads through notifyAll. However, notifyAll cannot distinguish whether the awakened thread is a read thread or a write thread.
However, through Condition, the wake-up read thread can be explicitly specified.