(十二)JDK源码分析之常用并发工具类

  • CountDownLatch

    • 功能
      1.需要等待多个线程都完成某个任务,然后唤醒主线程继续执行;
      2.当两个线程时,主线程需要等待子线程完成任务到某个进度,然后唤醒主线程继续执行。

    • 使用
      1.创建CountDownLatch对象,给一个初始值n,可以理解为进度数字n
      2.执行CountDownLatch的await方法,主线程会被阻塞,直到CountDownLatch设置的n等于0
      3.在你认为需要调用CountDownLatch的countDown方法地方调用,这个时候其实n就会减一
      4.如果你调用了countDown方法n次,那么主线程会从阻塞状态中被唤醒,主线程继续执行

    • 原理
      1.基于AQS实现的,利用AQS的同步功能
      2.CountDownLatch的await方法其实就是将当前线程加入到阻塞队列,然后挂起当前线程
      3.CountDownLatch的countDown方法其实就是尝试唤醒阻塞队列中的线程,当n=0的时候,主线程对应在阻 塞队 列的节点会被唤醒(n>0时会唤醒失败,但是每次都会尝试唤醒),唤醒后,主线程就继续执行。

    • 代码

      public void await() throws InterruptedException {
              sync.acquireSharedInterruptibly(1);
      }
      
      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) {
                  	//status值大于0,那么r会返回-1
                      int r = tryAcquireShared(arg);
                      if (r >= 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);
          }
      }
      
      	public void countDown() {
      	        sync.releaseShared(1);
      	}
      
      	 public final boolean releaseShared(int arg) {
      	       //tryReleaseShared方法只有在status==0的情况下才会返回true,否则只对status-1,
      	       //然后返回false
      	        if (tryReleaseShared(arg)) {
      	        	//doReleaseShared唤醒阻塞队列的线程,也就是头结点的next
      	            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) {
      	                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
      	                        continue;            // loop to recheck cases
      	                        //唤醒阻塞队列第一个线程(头结点不属于阻塞队列元素)
      	                    unparkSuccessor(h);
      	                }
      	                else if (ws == 0 &&
      	                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
      	                    continue;                // loop on failed CAS
      	            }
      	            if (h == head)                   // loop if head changed
      	                break;
      	        }
      	    }
      
  • CyclicBarrier

    • 功能
      1.需要等待多个子线程都完成某个任务,最后一个子线程或者主线程唤醒所有子线程;
      2.唤醒子线程们后,所有线程继续执行,或者也可以指定一个任务,在突破阻塞点的时候执行
      3.可以重复使用,一次阻塞点被解除后可以继续使用

    • 使用
      1.创建CyclicBarrier对象,给一个初始值n,也就是多少线程在阻塞点后继续执行
      2.在你的程序中调用CyclicBarrier的await方法,表示到达阻塞点,这个时候当前线程阻塞,这个方法既用于同步阻塞使用,也用于解除阻塞使用,当调用await是最后一个阻塞点的时候,会解除所谓的栅栏,这一点也是和CountDownLatch最大的区别,在实现方式上。

    • 原理
      1.基于AQS实现的,利用AQS的同步功能
      2.CyclicBarrier的await方法会将当前线程加入到等待队列,然后挂起当前线程,等到最后一个await方法调用的时候,会唤醒(将条件队列转移到阻塞队列)所有条件队列的节点,最后唤醒阻塞队列的第一个节点开始执行。

    • 代码

      private int dowait(boolean timed, long nanos)
              throws InterruptedException, BrokenBarrierException,
                     TimeoutException {
              final ReentrantLock lock = this.lock;
              lock.lock();
              try {
                  final Generation g = generation;
      
                  if (g.broken)
                      throw new BrokenBarrierException();
      
                  if (Thread.interrupted()) {
                      breakBarrier();
                      throw new InterruptedException();
                  }
      
      			//每次调用await方法都会将count减一
                  int index = --count;
                  //如果是最后一个await被调用,那么执行下面的逻辑
                  if (index == 0) {  // tripped
                      boolean ranAction = false;
                      try {
                      	//执行CyclicBarrier创建时指定的任务
                          final Runnable command = barrierCommand;
                          if (command != null)
                              command.run();
                          ranAction = true;
                          //将所有等待队列的节点加入到阻塞队列中,重新初始化,可以重复使用目的
                          nextGeneration();
                          return 0;
                      } finally {
                          if (!ranAction)
                              breakBarrier();
                      }
                  }
      
                  // loop until tripped, broken, interrupted, or timed out
                  //如果不是最后一个await,那么count>0,执行下面逻辑
                  for (;;) {
                      try {
                          if (!timed)
                          	//将当前线程加入到等待队列中,当前线程挂起
                              trip.await();
                          else if (nanos > 0L)
                              nanos = trip.awaitNanos(nanos);
                      } catch (InterruptedException ie) {
                          if (g == generation && ! g.broken) {
                              breakBarrier();
                              throw ie;
                          } else {
                              // We're about to finish waiting even if we had not
                              // been interrupted, so this interrupt is deemed to
                              // "belong" to subsequent execution.
                              Thread.currentThread().interrupt();
                          }
                      }
      
                      if (g.broken)
                          throw new BrokenBarrierException();
      
                      if (g != generation)
                          return index;
      
                      if (timed && nanos <= 0L) {
                          breakBarrier();
                          throw new TimeoutException();
                      }
                  }
              } finally {
              	//唤醒阻塞队列,让阻塞队列中任务开始执行
                  lock.unlock();
              }
          }
      
  • 总结
    使用上:

    • CountDownLatch定义的阻塞点,也就是那个n,并不代表多少个线程,只是代表countDown的执行次数
    • CyclicBarrier定义的阻塞点,也就是那个n,必须是要在n个线程中调用await方法,因为await会阻塞当前线程,所以调用多次也是徒劳,必须要n个线程调用才能消耗掉这个n

    原理上

    • CountDownLatch是直接基于阻塞队列的,一个CountDownLatch就是阻塞队列的一个节点,和n没关系
    • CyclicBarrier是基于条件队列的,n是多少就有多少个节点体现在等待队列中,和n有关系。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值