Java多线程系列——计数器 CountDownLatch

简介:

CountDownLatch 是一个非常实用的多线程控制工具类,通常用来控制线程的等待,它可以让某个线程等待直到倒计时结束

CountDownLatch 提供了两个主要的方法,await()、countDown()。

  • await:使当前线程阻塞,等待计数器为 0
  • countDown:计数器减一,计数为零时,释放所有在等待的线程

实例:

public class CountDownLatchDemo implements Runnable {
    static final CountDownLatch end = new CountDownLatch(10);
    static final CountDownLatchDemo demo = new CountDownLatchDemo();
    @Override
    public void run() {
        try {
            Thread.sleep(new Random().nextInt(10) * 1000);
            System.out.println("check complete...");
            end.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for (int i = 0;i < 10;i++) {
            exec.submit(demo);
        }
        end.await();
        System.out.println("Fire!");
        exec.shutdown();
    }
}

原理解析:

countDownLatch 的计数是通过一个共享变量(volatile)实现的,下面分析它的三个核心函数:构造函数,CountDownLatch(int count);阻塞线程,await();计数器减一,countDown()。

CountDownLatch(int count)

public CountDownLatch(int count) {
  if (count < 0) throw new IllegalArgumentException("count < 0");
  this.sync = new Sync(count);
}

其中 Sync 是 CountDownLatch 的内部类,并且 Sync 继承了 AbstractQueuedSynchronizer

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) {
    // Decrement count; signal when transition to zero
    for (;;) {
      int c = getState();
      if (c == 0)
        return false;
      int nextc = c-1;
      if (compareAndSetState(c, nextc))
        return nextc == 0;
    }
  }
}

其中 setState 是设置 AbstractQueuedSynchronizer 中 state 变量,该变量声明了 volatile。

State 就是 countDownLatch 中的计数器。

await()

public void await() throws InterruptedException {
  sync.acquireSharedInterruptibly(1);
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
      throws InterruptedException {
  if (Thread.interrupted())
    throw new InterruptedException();
  return tryAcquireShared(arg) >= 0 ||
    doAcquireSharedNanos(arg, nanosTimeout);
}

acquireSharedInterruptibly() 的作用是获取共享锁,如果当前线程处于中断状态,则抛出 InterruptedException,否则,调用 tryAcquireShared(arg) 尝试获取共享锁,如果锁计数器 = 0,则表示锁为可获取状态,返回 1,否则,锁为不可获取状态,则返回 -1。

doAcquireSharedNanos() 会使当前线程一直等待,直到当前线程获取到共享锁(或线程被中断)才返回。

countDown()

public void countDown() {
  sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
  if (tryReleaseShared(arg)) {
    doReleaseShared();
    return true;
  }
  return false;
}

releaseShared() 目的是让当前线程释放它所持有的共享锁。

tryReleaseShared() 的作用是释放共享锁,将锁计数器的值减一。

总结

CountDownLatch 是通过共享锁实现的。CountDownLatch 构造函数传递 int 参数,该参数是计数器的初始状态,表示共享锁最多能被 count 个线程同时获取。

当某线程调用 CountDownLatch 的 await 方法时,该线程会等待共享锁可用时(计数器为 0 时),才能获取共享锁,进而继续执行。

每次执行 countDown 时,会将计数器减一。

参考资料

Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

原文地址:https://www.cnblogs.com/zhengbin/p/9223635.html

时间: 2024-10-09 21:38:02

Java多线程系列——计数器 CountDownLatch的相关文章

Java多线程系列目录(转)

转载方便自己学习,转自:Java多线程系列目录(共43篇) http://www.cnblogs.com/skywang12345/p/java_threads_category.html 最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--"基础篇"01之 基本概念 02. Java多线程系列--"基础篇"02之 常用的实现多线程的两种方式 03. Java多线程系列--"基础篇"03之

Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3534050.html Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可

Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

ReentrantLock介绍 ReentrantLock是一个可重入的互斥锁,又被称为"独占锁". 顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有:而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取.ReentrantLock分为"公平锁"和"非公平锁".它们的区别体现在获取锁的机制上是否公平."锁"是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock在

Java多线程系列--“JUC锁”05之 非公平锁

获取非公平锁(基于JDK1.7.0_40) 非公平锁和公平锁在获取锁的方法上,流程是一样的:它们的区别主要表现在"尝试获取锁的机制不同".简单点说,"公平锁"在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而"非公平锁"在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁).在前面的"Java多线程系列--"JUC锁"03之 公平锁(一)&q

java多线程系列8-线程的优先级

在java中设置线程优先级使用setPriority,在jdk中的源代码如下: public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup())

Java多线程系列

参考资料: http://www.jianshu.com/p/40d4c7aebd66 0.环境 Java: jdk1.8.0_91 CPU: Intel Core i5-6500 Memory: 8G 1.说明 本系列文章为Java多线程的学习记录 Java多线程系列一--Java实现线程方法 Java多线程系列二--Thread类的方法 Java多线程系列三--实现线程同步的方法 Java多线程系列四--控制线程执行顺序 Java多线程系列五--列表类 Java多线程系列六--Map实现类

Java多线程系列十——BlockingQueue类

参考资料:http://ifeve.com/java-synchronousqueue/http://www.cnblogs.com/jackyuj/archive/2010/11/24/1886553.htmlhttp://ifeve.com/java-blocking-queue/ BlockingQueue的几个API认识 方法 说明 add(E e) 添加元素,超出队列size上限后抛异常 offer(E e) 添加元素,超出队列size上限后抛异常,相比add官方更建议使用offer方

java多线程系列(三)

等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解能让知识更加简单易懂. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知机制 java多线程系列(四)之ReentrantLock的使用 非等待通知 public void run() { try {

java多线程系列(一)

java多线程技能 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解能让知识更加简单易懂. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知机制 java多线程系列(四)之ReentrantLock的使用 并发历史 在没有操作系统的时候,一台计算机只执行一个程序,