Java - JUC - Common concurrent tool classes

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.

Published 22 original articles, won praise 3, visited 1685
Private letter follow

Posted by [n00b] on Mon, 16 Mar 2020 05:31:53 -0700