Java Concurrency analytical tools -CountDownLatch

basic concepts

We usually develop, you should come across such a demand: a function requires several threads together in collaboration, and then have to wait for these threads are processed, and to continue follow-up operation. Then we can choose to use this CountDownLatch concurrent toolkit.

Instructions

package com.demo;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
    	// 参数5表示我们需要等待的线程数
        CountDownLatch cdl = new CountDownLatch(5);
        // 启动5个子线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    // ...
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    // 子线程完成以后,调用CountDownLatch.countDown()方法
                    System.out.println(Thread.currentThread().getName() + "执行完成");
                    cdl.countDown();
                }
            }, "线程-" + (i+1)).start();
        }
        // 调用调用await方法后,主线程阻塞,并等待所有子线程执行完成
        cdl.await();
        System.out.println("子线程全部执行完成:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}
复制代码

Execution results are as follows:

线程-1执行完成
线程-4执行完成
线程-5执行完成
线程-2执行完成
线程-3执行完成
子线程全部执行完成:2020-03-12 12:42:41
复制代码

We see the main Chengkai Qi five sub-thread, called CountDownLatch.await () method emperor thread into the blocked state, when all the sub-thread execution is completed, the main thread can continue execution.

Internal principle

We track the source to look at the internals of the CountDownLatch. First, we first CountDownLatch a new object into its constructor:

public class CountDownLatch {
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // 新建一个同步器
        this.sync = new Sync(count);
    }
}
复制代码

Here created a new Sync objects, Sync is its internal class, we go look:

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        Sync(int count) {
        	// 将同步状态设置为指定的值
            setState(count);
        }
        int getCount() {
            return getState();
        }
        // 省略一些代码
		...
    }
复制代码

We see Sync inherited AbstractQueuedSynchronizer, CountDownLatch description is based AQS ( AQS implementation principle ) implemented into setState way here just sync state state assigned to the number of threads we pass.

public abstract class AbstractQueuedSynchronizer
	...
	// 将同步状态设置为5
    protected final void setState(int newState) {
        state = newState;
    }
}
复制代码

Above is a new logic CountDownLatch object, look below invoked await of what has been done into the await method:

public class CountDownLatch {
 	public void await() throws InterruptedException {
        // 父类AbstractQueuedSynchronizer中实现
        sync.acquireSharedInterruptibly(1);
    }
}
复制代码

acquireSharedInterruptibly This method is implemented in the parent class AbstractQueuedSynchronizer Sync:

public abstract class AbstractQueuedSynchronizer
	...
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
         // 获取共享锁
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
	// 这边空实现,其它是交给子类去实现,我们这边是Sync
    protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();
    }
}
复制代码

We go back to Sync, the look tryAcquireShared implementation of this method:

    protected int tryAcquireShared(int acquires) {
   	   return (getState() == 0) ? 1 : -1;
    }
复制代码

Here is simple, relatively synchronization status state, equal to 0 returns 1, anti--1 is returned from the beginning we became 5 initialization, so here return -1. Back to AbstractQueuedSynchronizer in:

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 这边返回确实小于0
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
复制代码

Because the front tryAcquireShared return is -1, the condition is satisfied if the side, into the doAcquireSharedInterruptibly method:

public abstract class AbstractQueuedSynchronizer
	...
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 当前线程包装成node放入阻塞队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null;
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}
复制代码

Here we are only concerned at parkAndCheckInterrupt method:

 private final boolean parkAndCheckInterrupt() {
 		// 阻塞当前线程
        LockSupport.park(this);
        return Thread.interrupted();
    }
复制代码

Analysis Up to now, we know that the main thread calls await completion method would have been up here. So how is it to be awakened it? Then we continue to track it to achieve CountDownLatch.countDown, we entered this approach:

public class CountDownLatch {
	...
    public void countDown() {
    	// 调用父类AbstractQueuedSynchronizer的方法
        sync.releaseShared(1);
    }
}
复制代码

Also releaseShared This method is implemented in the parent class in AbstractQueuedSynchronizer:

public abstract class AbstractQueuedSynchronizer
    public final boolean releaseShared(int arg) {
    	// 模板方法,调用子类Sync的tryReleaseShared方法
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
}
复制代码

tryReleaseShared is implemented in Sync in:

protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                // 同步状态已经是0了就直接返回false
                if (c == 0)
                    return false;
                int nextc = c-1;
                // 使用CAS操作:同步状态减1
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
复制代码

Logic of this code is that whenever called once countDown method, synchronization status state will subtract 1, it will return true if the synchronization state state goes to zero, so when we finished the last sub-thread execution method countDown it It will return true. We return to releaseShared methods:

public abstract class AbstractQueuedSynchronizer
   public final boolean releaseShared(int arg) {
   		// 当最后一个线程执行完,tryReleaseShared返回true
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
 }
复制代码

We know that the last thread execution is completed, tryReleaseShared returns true, it will enter doReleaseShared method:

   private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;  
                    // 唤醒阻塞的线程
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            if (h == head)
                break;
        }
    }
复制代码

Here we are only concerned at unparkSuccessor method:

    private void unparkSuccessor(Node node) {
        ...// 省略一些代码
        if (s != null)
        	// 唤醒线程
            LockSupport.unpark(s.thread);
    }
复制代码

Ultimately, we see that when the last child thread is finished, the main thread will be awakened.

Guess you like

Origin juejin.im/post/5e69d597e51d452717263f8f