jdk1.5开始concurrent包里提供的,并发编程工具类。 node
CountDownLatch这个类可以使一个线程等待其余线程完成各自的工做后再执行。CountDownLatch容许一个或多个线程等待其余线程完成操做。 算法
例如,应用程序的主线程但愿在负责启动框架服务的线程已经启动全部的框架服务以后再执行。编程
CountDownLatch很是适合于对任务进行拆分,使其并行执行,好比某个任务执行2s,其对数据的请求能够分为五个部分,那么就能够将这个任务拆分为5个子任务,分别交由五个线程执行,执行完成以后再由主线程进行汇总,此时,总的执行时间将决定于执行最慢的任务,平均来看,仍是大大减小了总的执行时间。bash
CountDownLatch是不能复用的,不可能从新初始化或者修改CountDownLatch对象的内部计数器的值。
多线程
public void countDown() {
sync.releaseShared(1);
}复制代码
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState(); // 获取当前state属性的值
if (c == 0) // 若是state为0,则说明当前计数器已经计数完成,直接返回
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc)) // 使用CAS算法对state进行设置
return nextc == 0; // 设置成功后返回当前是否为最后一个设置state的线程
}
}
}复制代码
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}复制代码
private void doReleaseShared() {
for (;;) {
Node h = head; // 记录等待队列中的头结点的线程
if (h != null && h != tail) { // 头结点不为空,且头结点不等于尾节点
int ws = h.waitStatus;
if (ws == Node.SIGNAL) { // SIGNAL状态表示当前节点正在等待被唤醒
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) // 清除当前节点的等待状态
continue;
unparkSuccessor(h); // 唤醒当前节点的下一个节点
} else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head) // 若是h仍是指向头结点,说明前面这段代码执行过程当中没有其余线程对头结点进行过处理
break;
}
}复制代码
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 清除当前节点的等待状态
Node s = node.next;
if (s == null || s.waitStatus > 0) { // s的等待状态大于0说明该节点中的线程已经被外部取消等待了
s = null;
// 从队列尾部往前遍历,找到最后一个处于等待状态的节点,用s记录下来
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒离传入节点最近的处于等待状态的节点线程
}复制代码
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
复制代码
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}复制代码
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
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) { // 大于0表示获取了执行权限
setHeadAndPropagate(node, r); // 将当前节点设置为头结点,而且唤醒后面处于等待状态的节点
p.next = null; // help GC
failed = false;
return;
}
}
// 走到这一步说明没有获取到执行权限,就使当前线程进入“搁置”状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}复制代码
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node); // 将当前节点设置为头节点
// 检查唤醒过程是否须要往下传递,而且检查头结点的等待状态
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared()) // 若是下一个节点是尝试以共享状态获取获取执行权限的节点,则将其唤醒
doReleaseShared();
}
}复制代码
public class CountdownLatchExample {
public static void main(String[] args) throws InterruptedException {
final int totalThread = 10;
CountDownLatch countDownLatch = new CountDownLatch(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < totalThread; i++) {
executorService.execute(() -> {
System.out.print("run..");
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("end");
executorService.shutdown();
}
}
run..run..run..run..run..run..run..run..run..run..end复制代码
CountDownLatch的做用就是容许一个或多个线程等待其余线程完成操做,看起来有点相似join() 方法,但其提供了比 join() 更加灵活的API。CountDownLatch能够手动控制在n个线程里调用n次countDown方法使计数器进行减一操做,也能够在一个线程里调用n次执行减一操做。而 join() 的实现原理是不停检查join线程是否存活,若是 join 线程存活则让当前线程永远等待。因此二者之间相对来讲仍是CountDownLatch使用起来较为灵活。并发
参考自:《Java并发编程的艺术》和www.jianshu.com/p/128476015…框架