美文网首页
CountDownLatch思考

CountDownLatch思考

作者: 追梦小蜗牛 | 来源:发表于2019-12-18 21:10 被阅读0次
person-walking-on-street-3325720.jpg

简介:

CountDownLatch是并发包的一个类,我个人的理解是它是为了解决多个线程间协作的问题的或者一个大的任务分割成多个小任务,然后用多个子线程去并行完成,当所有的子任务都完成之后,主流程才继续执行...一般解决线程间的协作问题的方法有,像原始的wait、notify方法,还有join等,不过这些都是一对一的,今天的主角解决的是多对多的协作问题。

类图:

CountDownLatch.png

从类图上面可以获取到一些关键信息:

  • Sync是一个静态、不可变的内部类,父类是AbstractQueuedSynchronizer(AQS)
  • 继承AQS的state、重写tryAcquireShared()方法、tryReleaseShared()方法
  • 把Sync构造函数的count变量赋值给state,然后对state的值进行减法操作;赋予这个state变量和上面那两个重写的方法新的含义(也就是CountDownLatch实现的功能)。
  • AQS还是很灵活,扩展性很好,只要你自己想操作,可以定义一个自己的同步器,只需要继承AQS这个类,重新赋予state、tryAcquireShared()、tryReleaseShared()新的含义和逻辑。
    隐含的一些信息:
  • state被volatile修饰符修饰...
  • doAcquireShared()这个私有方法是把当前操作线程抽象成一个Node,然后利用CAS添加到链表的末尾;
  • 最终线程的阻塞是由LockSupport.park()这个方法的调用实现的,进去之后发现:U.park(false, 0L),这个方法是Unsafe类里面的一个native方法,这个就是jvm提供的一个native接口,让上层调用者和操作系统交互。其实调用完这个方法之后,其实从操作系统的角度来看,这个线程暂时是不会被CPU调度选中调度的,所以才会阻塞...

举例:

例子其实也都是源码上面官方的,感觉人家写的更有代表性和通用性,所以就拿过来分析了。

场景1:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
一个主任务在执行业务的过程中,需要等待一些中间业务执行完成,而这些中间业务为了提高效率,可以采用多个线程来完成,当所有这些中间业务都执行完了之后,主任务才继续运行下面的业务逻辑。
例子1:

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(5);
        System.out.println(Thread.currentThread().getName() + "正在工作......");
        for (int i = 0; i < 5; i++) {//创建5个子任务,并启动
            new Thread(new Worker(startSignal, doneSignal)).start();
        }
        startSignal.countDown();//由于count是1,所以调用countDown一次就变成0了,其他由于调用startSignal的await方法阻塞的线程都会恢复运行
        doneSignal.await();//子任务在运行,当前主任务阻塞,需要等待所有的子任务都执行完毕之后,才能接着运行下面的业务代码
        System.out.println("所有的子任务都执行完毕了。");
        System.out.println(Thread.currentThread().getName() + "工作也已经完成。");
    }

    static class Worker implements Runnable {

        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;

        public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }

        @Override
        public void run() {
            try {
                startSignal.await();//调用await方法的当前线程会阻塞
                System.out.println(Thread.currentThread().getName() + "开始工作......");
                doneSignal.countDown();//每一个线程执行完毕的时候,都会调用一次doneSignal.countDown方法,做一次减法
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

场景2:

Another typical usage would be to divide a problem into N parts,describe each part with a Runnable that executes that portion and counts down on the latch
场景是把一个大的任务分割成N个小任务,然后这N个小任务提交给线程池处理,负责协调的线程等待,直到它们都完成。

public static void main(String[] args) throws InterruptedException {
        CountDownLatch doneSignal = new CountDownLatch(5);
        Executor executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executor.execute(new Worker(doneSignal,i));
        }
        doneSignal.await();
    }

    static class Worker implements Runnable {

        private CountDownLatch doneSignal;
        private int i;

        public Worker(CountDownLatch doneSignal, int i) {
            this.doneSignal = doneSignal;
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" 正在工作......");
            doneSignal.countDown();
        }
    }

总结:

越来越感觉Java里面的东西好多呀,要想Hold住全场,还需更多努力呀。先是一个点一个点的突破,然后经常性的串一串,连贯一下知识,不至于让知识太零碎。这样在解决复杂问题的时候,才会游刃有余,一点一点突破,一点一点进步。
缺乏自信是因为害怕出丑,出洋相,被拒绝,被嘲笑,被鄙视。每个人都不可能不会被嘲笑、出丑,接受这个事实,拥抱这个事实,坦然接受就不会害怕了。

相关文章

网友评论

      本文标题:CountDownLatch思考

      本文链接:https://www.haomeiwen.com/subject/ctuenctx.html