• 多线程工具类CountDownLatch


    CountDownLatch

    0、基础

    • countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。

    • 存在于java.util.cucurrent包下。

    • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。

    • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。


    1、初始化CountDownLatch

    final CountDownLatch countDownLatch = new CountDownLatch(3);
    这里的3 是初始化的数字
    观察源码,可以知道这个是通过修改volatile关键字来实现的
    
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    
        Sync(int count) {
            setState(count);
        }
    
            protected final void setState(int newState) {
                state = newState;
            }
    
            private volatile int state;
    

    2、常用方法

    2.1、countDownLatch.await();

    阻塞线程,知道共享变量state为0继续执行代码

    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);
        }
    		
    		//只要初始值的state不为0就返回-1,也就是满足if条件小于0
            protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
            }
    
            protected final int getState() {
                return state;
            }
    
            private void doAcquireSharedInterruptibly(int arg)
                throws InterruptedException {
                final Node node = addWaiter(Node.SHARED);
                boolean failed = true;
                try {
                    //相当于while(true) 使用中断来打断死循环
                    for (;;) {
                        final Node p = node.predecessor();
                        if (p == head) {
                        //获取volatile共享变量state的值判断   一旦state为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);
                }
            }
    

    2.2、countDownLatch.countDown();

    原理cas方式去让state--,

    public void countDown() {
        sync.releaseShared(1);
    }
    
        public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {
                doReleaseShared();
                return true;
            }
            return false;
        }
    
            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;
                    //cas方式去让state-1
                    if (compareAndSetState(c, nextc))
                        return nextc == 0;
                }
            }
    
    private void doReleaseShared() {
    //确保发布传播,即使还有其他正在进行的获取发布。如果需要信号,这会以通常的方式尝试 unparkSuccessor 的 head。但如果没有,则将状态设置为 PROPAGATE 以确保在发布时继续传播。此外,我们必须循环以防在我们执行此操作时添加了新节点。此外,与 unparkSuccessor 的其他用法不同,我们需要知道 CAS 重置状态是否失败,如果是则重新检查。
            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;
            }
        }
    

    2.3、getCount()

    获取当前的state


    3、实战

    初始化CountDownLatch,初始值为3,首先启动t2,t3线程,在执行System.out.println后执行countDownLatch.await();,进入阻塞状态,Thread.sleep(2000);是确保t2,t3进入阻塞,然后将countDownLatch.countDown,知道减到0,这样之前阻塞的线程会继续执行后续的代码。

    public static void main(String[] args) {
    
    //通过构造方法传入初始值   底层是 volatile
          final CountDownLatch countDownLatch = new CountDownLatch(3);
    
          new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      System.out.println("t2 start waitting for countDownLatch to zero");
                      countDownLatch.await();
                      System.out.println("t2 stop");
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }, "t2").start();
    
          new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      System.out.println("t3 start waitting for countDownLatch to zero");
                      countDownLatch.await();
                      System.out.println("t3 stop");
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }, "t3").start();
    
          try {
              Thread.sleep(2000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
    
          new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      Thread.sleep(1000);
                      System.out.println("countDownLatch.countDown1");
                      countDownLatch.countDown(); //3-1=2
                      Thread.sleep(1000);
                      System.out.println("countDownLatch.countDown2");
                      countDownLatch.countDown(); //2-1=1
                      Thread.sleep(1000);
                      System.out.println("countDownLatch.countDown3");
                      countDownLatch.countDown();    //1-1=0
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }, "t1").start();
      }
    

    控制台输出

    image-20220222163838535

  • 相关阅读:
    C语言的选择结构和条件判断
    学习C语言必须知道的理论知识(第三章数据类型的分类)
    基本输入输出函数以及其格式.
    学习C语言必须知道的理论知识(第三章常量和变量)
    简单的算术题。
    学习C语言必须知道的理论知识(第二章算法)
    学习C语言必须知道的理论知识(第一章)
    学习C语言必须知道的理论知识(第三章常量类型,运算符和表达式)
    C语言中的循环控制语句.
    彻底弄懂JS中的this
  • 原文地址:https://www.cnblogs.com/yslu/p/15923854.html
Copyright © 2020-2023  润新知