Java多线程同步工具类之CountDownLatch

  在过去咱们实现多线程同步的代码中,每每使用join()、wait()、notiyAll()等线程间通讯的方式,随着JUC包的不断的完善,java为咱们提供了丰富同步工具类,官方也鼓励咱们使用工具类来实现多线程的同步,今天咱们就对其中CountDownLatch类的使用与底层实现进行分析与总结。
  
  1、CountDownLatch使用
  
  CountDownLatch其实能够看作一个计数器,统计多个线程执行完成的状况,适用于控制一个或多个线程等待,直到全部线程都执行完毕的场景,相似与Thread.join()的做用。下面咱们经过一个简单的例子看下CountDownLatch的使用。
  
  复制代码
  
  public static void main(String[] args) {
  
  final CountDownLatch countDownLatch = new CountDownLatch(5);
  
  // 启动计数线程
  
  for (int i = 0; i < 5; i++) {
  
  new CountDownLatchThread(i, countDownLatch).start();
  
  }
  
  // 启动等待线程
  
  for (int i = 0; i < 5; i++) {
  
  new Thread() {
  
  public void run() {
  
  try {
  
  countDownLatch.await(www.bdqxylgw.com );
  
  } catch (InterruptedException e) {
  
  // TODO Auto-generated catch block
  
  e.printStackTrace();
  
  }
  
  System.out.println("计数完毕了," + Thread.currentThread().getName() + "等待线程执行");
  
  }
  
  }.start();
  
  }
  
  }
  
  复制代码
  
  计数线程代码:
  
  复制代码
  
  public class CountDownLatchThread extends Thread {
  
  private CountDownLatch www.rmutk.net countDownLatch;
  
  private int name;
  
  private int count;
  
  public CountDownLatchThread(int name, CountDownLatch countDownLatch) {
  
  this.name = name;
  
  this.countDownLatch =www.chaoyuuyule.com countDownLatch;
  
  this.count = 0;
  
  }
  
  public void run() {
  
  try {
  
  for (int i = 0; i < 10; i++) {
  
  Thread.sleep(100);
  
  count++;
  
  }
  
  System.out.println(name + "号线程--" + Thread.currentThread().getName() + "--计数完成了");
  
  countDownLatch.countDown(www.yaoshiyulegw.com);
  
  } catch (Exception e) {
  
  // TODO Auto-generated catch block
  
  e.printStackTrace(www.yifayuled.cn);
  
  }
  
  }
  
  }
  
  复制代码
  
  输出结果:
  
  复制代码
  
  1号线程--Thread-1--计数完成了
  
  0号线程--Thread-0--计数完成了
  
  4号线程--Thread-4--计数完成了
  
  2号线程--Thread-2--计数完成了
  
  3号线程--Thread-3--计数完成了
  
  计数完毕了,Thread-5等待线程执行
  
  计数完毕了,Thread-6等待线程执行
  
  计数完毕了,Thread-7等待线程执行
  
  计数完毕了,Thread-8等待线程执行
  
  计数完毕了,Thread-9等待线程执行
  
  复制代码
  
  经过上面的例子能够看到,利用CountDownLatch的countDown方法与await()方法,咱们能够同步计数线程与等待线程,使等待线程在全部计数线程完成后再开始运行。
  
  2、CountDownLatch源码分析
  
  接下来咱们对countDownLatch内部源码进行一下分析。
  
  一、CountDownLatch的构造。
  
  首先看下CountDownLatch的构造函数
  
  public CountDownLatch(int count) {
  
  if (count <www.xcdeyiju.com 0) throw new IllegalArgumentException("count < 0");
  
  this.sync = new Sync(count);
  
  }
  
  CountDownLatch的构造函数会接收一个count值作为计数器,也就是若是你须要等待N个线程执行结束,那这里就传入N。同时CountDownLatch会实例化一个Sync对象,这个Sync实际上是CountDownLatch内部定义的一个继承自AbstractQueuedSynchronizer的实现类,因此CountDownLatch提供的同步和其余功能都是围绕Sync这个子类实现的,也就是基于AbstractQueuedSynchronizer类来实现的。
  
  咱们来看下Sync这个类的定义
  
  复制代码
  
  private static final class Sync extends AbstractQueuedSynchronizer {
  
  private static final long serialVersionUID = 4982264981922014374L;
  
  /**
  
  一、设置AbstractQueuedSynchronizer中同步状态的值state,也就是计数器的值。
  
  二、这个值volatile变量,必须保证线程间的可见性;
  
  **/
  
  Sync(int count) {
  
  setState(count);
  
  }
  
  int getCount() {
  
  return getState();
  
  }
  
  //获取同步状态的值
  
  protected int tryAcquireShared(int acquires) {
  
  return (getState() == 0) ? 1 : -1;
  
  }
  
  protected boolean tryReleaseShared(int releases) {
  
  // 经过CAS操做改变同步状态值,保证同步状态的值state的线程安全。
  
  for (;;) {
  
  int c = getState();
  
  if (c == 0)
  
  return false;
  
  int nextc = c-1;
  
  if (compareAndSetState(c, nextc))
  
  return nextc == 0;
  
  }
  
  }
  
  }
  
  复制代码
  
  二、countDown方法
  
  首先咱们看下countDown方法的源码
  
  public void countDown() {
  
  //改变同步状态值,线程执行完成时计数器减一
  
  sync.releaseShared(1);
  
  }
  
  AbstractQueuedSynchronizer类中releaseShared() 方法的源码
  
  复制代码
  
  public final boolean releaseShared(int arg) {
  
  // CountDownLatch定义的子类Sync实现,经过CAS操做改变State的值
  
  if (tryReleaseShared(arg)) {
  
  //State以递减为0,表明着全部执行线程执行完毕,共享模式下释放锁,那么等待线程就可以拿到锁往下执行。
  
  doReleaseShared();
  
  return true;
  
  }
  
  return false;
  
  }
  
  复制代码
  
  当调用CountDownLatch的countDown方法时,就会执行计数器进行减一操做,直到全部线程所有执行完毕,计算器为0时唤醒等待线程。
  
  AbstractQueuedSynchronizer中doReleaseShared方法是执行共享模式下释放锁的操做,从而让等待线程获取锁,继续向下执行。
  
  三、await方法
  
  public void await() throws InterruptedException {
  
  sync.acquireSharedInterruptibly(1);
  
  }
  
  AbstractQueuedSynchronizer类中acquireSharedInterruptibly() 方法的源码
  
  复制代码
  
  public final void acquireSharedInterruptibly(int arg)
  
  throws InterruptedException {
  
  if (Thread.interrupted())
  
  throw new InterruptedException();
  
  //获取同步状态值
  
  if (tryAcquireShared(arg) < 0)
  
  //同步状态值即计数器的值不为0,等待线程共享模式下尝试获取锁,获取不到锁的话进入阻塞
  
  doAcquireSharedInterruptibly(arg);
  
  }
  
  复制代码
  
  await方法的实现也很明确,首页获取同步状态也就是计数器的值,若是为0即全部线程执行完毕返回1,不然返回-1的话,等待线程在共享模式下尝试获取锁,获取不到锁的话进入阻塞;
  
  AbstractQueuedSynchronizer中doAcquireSharedInterruptibly方法是执行共享模式下获取锁的操做;
  
  3、总结
  
  经过上面分析能够看到CountDownLatch是基于AbstractQueuedSynchronizer类实现的,一个很是实用的多线程控制工具类,它相似与一个计数器用来控制指定的线程等待,直到计数器归零。以上咱们对CountDownLatch类的使用与核心方法的源码进入了必定的分析,其中若有不足与不正确的地方还望指出与海涵。java

相关文章
相关标签/搜索