Condition
Condition is a tool class for multi-threaded communication in JUC, which allows some threads to wait for conditions together. The thread will only wake up if the condition is met.
Similar to wait/notify. There is a similar implementation in Condition.
public class ConditionAwait implements Runnable{ private Lock lock; private Condition condition; public ConditionAwait(Lock lock, Condition condition) { this.lock = lock; this.condition = condition; } @Override public void run() { System.out.println("begin -- ConditionAwait start"); lock.lock(); try { // Block pending current thread condition.await(); System.out.println("end -- ConditionAwait"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } public class ConditionSignal implements Runnable{ private Lock lock; private Condition condition; public ConditionSignal(Lock lock, Condition condition) { this.lock = lock; this.condition = condition; } @Override public void run() { System.out.println("begin -- condition signal"); lock.lock(); // Wake up the blocked thread signal wake up a signal wake up all condition.signal(); System.out.println("end -- condition signal"); lock.unlock(); } } public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(new ConditionAwait(lock, condition)).start(); new Thread(new ConditionSignal(lock, condition)).start(); } // output begin -- ConditionAwait start begin -- condition signal end -- condition signal end -- ConditionAwait
As the code example, we start two threads. Both threads use the same Lock and Condition object. It can be found by the running results of the code. When the code runs to condition.await(), the thread will be suspended. Waiting for another thread to call the condition.signal() method will wake up the blocked thread. It is worth noting that if the thread blocking at the condition.await() method is not waked up by the condition.signal() method and the condition.signalAll() method, the thread will always be blocked.
Condition source code analysis
Calling Lock.lock() requires a lock lock, so it means there will be an AQS synchronization queue. The initial situation is as follows
After thread A obtains the lock, the state is updated from 0 to 1. Then thread A calls the condition.await() method. It will cause the current thread to enter the waiting queue and release the lock, and the thread state will change to the waiting state.
await() method
public final void await() throws InterruptedException { // Indicates that calling the await method is allowed to be interrupted if (Thread.interrupted()) throw new InterruptedException(); // Create a new node with the node status of condition. This method constructs a new condition queue // The queue here is not a two-way list, but a one-way list Node node = addConditionWaiter(); // Release the current lock, get the status of the lock, and wake up the thread of the next node of the head node from the AQS queue // Release the lock completely int savedState = fullyRelease(node); int interruptMode = 0; // Judge whether the current node is on the synchronization queue. If the signal signal has not been received, the current thread will be blocked while (!isOnSyncQueue(node)) { // Suspend current thread LockSupport.park(this); // if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } // When the thread wakes up, it tries to acquire the lock if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; // Clean up the node s on the condition queue if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
When the addConditionWaiter() method is completed, A condition queue is constructed. Thread A will be added to the condition queue. Until it is awakened by other threads with signal, it will be rejoined into AQS queue to compete for locks.
signal() method
The await() method blocks ThreadA, and ThreadB preempties the execution permissions of the lock, and ThreadB invokes the signal method of Condition. The node in the condition queue will wake up.
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); // Get a node in the condition queue Node first = firstWaiter; if (first != null) doSignal(first); }
doSignal() method
private void doSignal(Node first) { do { // Remove the first node from the condition queue if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; // CAS modifies the node status and puts the node in the AQS queue. Then wake up the thread of this node. } while (!transferForSignal(first) && (first = firstWaiter) != null); }
CountDownLatch
countDownLatch is a synchronization tool class that allows one or more threads to wait until other threads have finished executing.
public void test() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(3); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"In execution"); // Call countDown method, counter minus 1 equals 2 countDownLatch.countDown(); System.out.println(Thread.currentThread().getName()+"completion of enforcement"); }, "t1").start(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"In execution"); // Call countDown method, counter minus 1 equals 1 countDownLatch.countDown(); System.out.println(Thread.currentThread().getName()+"completion of enforcement"); }, "t2").start(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"In execution"); // Call countDown method, counter minus 1 equals 0 countDownLatch.countDown(); System.out.println(Thread.currentThread().getName()+"completion of enforcement"); }, "t3").start(); // Use countDownLatch. await() method to block the main thread // When the value of CountDownLatch counter is 0, the main thread will be released countDownLatch.await(); System.out.println("All threads completed"); }
Use CountDownLatch to simulate concurrent scenarios.
public class ConcurrenceDemo extends Thread{ private static CountDownLatch countDownLatch = new CountDownLatch(1); @Override public void run() { try { // Block current thread countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadName:"+Thread.currentThread().getName()); } public static void main(String[] args) { // Create 1000 threads, blocked at the same time for (int i = 0; i < 1000; i++) { new ConcurrenceDemo().start(); } // Counter-1 all threads are released at the same time countDownLatch.countDown(); } }
Summary: countDown() method will reduce the value of state by 1 every time it is called, until the value of state is 0; and await is a blocking method, when the value of state is reduced to 0, the await method will return. All threads calling the await method are blocked in the blocking queue of AQS, waiting to be woken up.
Semaphore
semaphore, which can control the number of threads accessed at the same time.
In the following code, we create the Semaphore object and set the parameter to 5 to allow 5 threads to execute the target code at the same time.
public class SemaphoreDemo { public static void main(String[] args) { // Indicates that there are five threads to get execution right at the same time Semaphore semaphore = new Semaphore(5); for (int i = 0; i < 10; i++) { new Car(i, semaphore).start(); } } static class Car extends Thread { private int num; private Semaphore semaphore; public Car(int num, Semaphore semaphore) { this.num = num; this.semaphore = semaphore; } @Override public void run() { try { // Get a license semaphore.acquire(); System.out.println("The first"+num+"Occupy a parking space"); TimeUnit.SECONDS.sleep(2); System.out.println("The first"+num+"The car is gone."); semaphore.release(); } catch (Exception e) { } } } }
Usage scenario: it is suitable for current limiting operation.
CyclicBarrier
CyclicBarrier literally means a cyclic barrier. The function of this tool is to let a group of threads get blocked when they reach a barrier, and the barrier will not open until the last thread reaches the barrier, and then all the blocked barriers will continue to work. The default construction method of CyclicBarrier is CyclicBarrier(int parties), whose parameters represent the number of barrier intercepting threads.
Usage scenario: when all subtasks need to be completed before the main task is executed, you can choose to use the CyclicBarrier.