<small id='SFxfG0R8L'></small> <noframes id='vZVP'>

  • <tfoot id='q2whHMCf'></tfoot>

      <legend id='7l2doHP'><style id='hYPLufkadD'><dir id='ZNB0ujwrg'><q id='14t5glmysr'></q></dir></style></legend>
      <i id='Xcqx8H6'><tr id='aK9uXt'><dt id='3QBTksCO'><q id='fJd3C8ea6g'><span id='XYTJU'><b id='h2wE'><form id='togSs6I'><ins id='7INRX23'></ins><ul id='lYi0Bj'></ul><sub id='ceb8kuT'></sub></form><legend id='2aBGMJO'></legend><bdo id='DTPr'><pre id='sBLzObQYgu'><center id='cFqR0a1X8k'></center></pre></bdo></b><th id='mfrjaQwS5D'></th></span></q></dt></tr></i><div id='tYAX51vz0p'><tfoot id='BTifCq'></tfoot><dl id='zyMunfNa'><fieldset id='apJL73uN'></fieldset></dl></div>

          <bdo id='jSICfM'></bdo><ul id='jthelFV3SK'></ul>

          1. <li id='9QRzAWcUgy'></li>
            登陆

            并发编程中CountDownLatch类的源码剖析

            admin 2019-11-05 223人围观 ,发现0个评论

            简介

            CountDownLatch从字面上了解,CountDown是倒计时的意思,Latch是锁、门栓,那么CountDownLatch便是倒计时的门栓了

            在开发时,咱们或许会有相似下面的这种场景,有三个并发使命一起去履行,然后鄙人一步需求等候它们都履行完结才干持续履行,完结这个现在想想大概有几种办法,Thread.join等候线程,运用wait()notify(),Executor.invokeAll、然后对各个Future调用get等,这些都能够完结,可是有没有一种专门的东西类完结这个作业呢,有的,在JDK1.5中供给了java.util.concurrent.CountDownLatch类,用于一个或多个类等候一向到其他线程完结一系列操作。

            CountDownLatch创立时设置一个count值,表明倒计时的次数,然后等候状况的线程调用CountDownLatch的await()办法(留意不要和Object.wait()混杂)进行等候,倒计时的办法是countDown(), 每次countDown都会削减count的值,直到count为0,则一切的await()的线程都会从等候中回来。

            countDown()和await()

            CountDownLatch首要有两个办法:countDown()和await()。countDown()办法用于使计数器减一,其一般是履行使命的线程调用,await()办规律使调用该办法的线程处于等候状况,其一般是主线程调用。这儿需求留意的是,countDown()办法并没有规则一个线程只能调用一次,当同一个线程调用屡次countDown()办法时,每次都会使计数器减一;别的,await()办法也并没有规则只能有一个线程履行该办法,假并发编程中CountDownLatch类的源码剖析如多个线程一起履行await()办法,那么这几个线程都将处于等候状况,而且以同享形式享有同一个锁。如下是其运用示例:

            在上面的比方中,首要声明晰一个CountDownLatch目标,而且由主线程创立了5个线程,别离履行使命,在每个使命中,当时线程会休眠2秒。在发动线程之后,主线程调用了CountDownLatch.await()办法,此刻,主线程将在此处等候创立的5个线程履行完使命之后才持续往下履行。如下是履行成果:

            从输出成果能够看出,主线程先发动了五个线程,然后主线程进入等候状况,当这五个线程都履行完使命之后主线程才完毕了等候。上述代码中需求留意的是,在履行使命的线程中,运用了try...finally结构,该结构能够确保创立的线程发作反常时CountDownLatch.countDown()办法也会履行,也就确保了主线程不会一向处于等候状况。

            CountDownLatch运用

            CountDownLatch十分适合于对使命进行拆分,使其并行履行,比方某个使命履行2s,其对数据的恳求能够分为五个部分,那么就能够将这个使命拆分为5个子使命,别离交由五个线程履行,履行完结之后再由主线程进行汇总,此刻,总的履行时刻将决定于履行最慢的使命,均匀来看,仍是大大削减了总的履行时刻。

            别的一种比较适宜运用CountDownLatch的当地是运用某些外部链接恳求数据的时分,比方图片。在本人所从事的项目中就有相似的状况,由于咱们运用的图片服务只供给了获取单个图片的功用,而每次获取图片的时刻不等,一般都需求1.5s~2s。当咱们需求批量获取图片的时分,比方列表页需求展现一系列的图片,假如运用单个线程次序获取,那么等候时刻将会极长,此刻咱们就能够运用CountDownLatch对获取图片的操作进行拆分,并行的获取图片,这样也就缩短了总的获取时刻。

            保护了一个volatile类型的整数state

            CountDownLatch是依据AbstractQueuedSynchronizer完结的,在AbstractQueuedSynchronizer中保护了一个volatile类型的整数state,volatile能够确保多线程环境下该变量的修正对每个线程都可见,而且由于该特点为整型,因此对该变量的修正也是原子的。创立一个CountDownLatch目标时,所传入的整数n就会赋值给state特点,当countDown()办法调用时,该线程就会测验对state减一,而调用await()办法时,当时线程就会判别state特点是否为0,假如为0,则持续往下履行,假如不为0,则使当时线程进入等候状况,直到某个线程将state特点置为0,其就会唤醒在await()办法中等候的线程。如下是countDown()办法的源代码:

            public void countDown() {
            sync.releaseShared(1);
            }

            这儿sync也即一个承继了并发编程中CountDownLatch类的源码剖析AbstractQueuedSynchronizer的类实例,该类是CountDownLatch的一个内部类,其声明如下:

            这儿tryReleaseShared(int)办法即对state特点进行减一操作的代码。能够看到,CAS也即compare and set的缩写,jvm会确保该办法的原子性,其会比较state是否为c,假如是则将其设置为nextc(自减1),假如state不为c,则阐明有别的的线程在getState()办法和compareAndSetState()办法调用之间对state进行了设置,当时线程也就没有成功设置state特点的值,其会并发编程中CountDownLatch类的源码剖析进入下一次循环中,如此往复,直至其成功设置state特点的值,即countDown()办法调用成功。

            在countDown()办法中调用的sync.releaseShared(1)调用时实践仍是调用的tryReleaseShared(int)办法,如下是releaseShared(int)办法的完结:

            public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
            }
            return false;
            }

            能够看到,在履行sync.releaseShared(1)办法时,其在调用tryReleaseShared(int)办法时会在无限for循环中设置state特点的值,设置成功之后其会依大竹爱子据设置的回来值(此刻state现已自减了一),即当时线程是否为将state特点设置为0的线程,来判别是否履行if块中的代码。doReleaseShared()办法首要作用是唤醒调用了await()办法的线程。需求留意的是,假如有多个线程调用了await()办法,这些线程都是以同享的办法等候在await()办法处的,试想,假如以独占的办法等候,那么当计数器削减至零时,就只有一个线程会被唤醒履行await()之后的代码,这明显不符合逻辑。如下是doReleaseShared()办法的完结代码:

            在doReleaseShared()办法中(一直留意当时办法是最终一个履行countDown()办法的线程履行的),首要判别头结点不为空,且不为尾节点,阐明等候行列中有等候唤醒的线程,这儿需求阐明的是,在等候行列中,头节点中并没有保存正在等候的线程,其仅仅一个空的Node目标,真实等候的线程是从头节点的下一个节点开端寄存的,因此会有仇人结点是否等于尾节并发编程中CountDownLatch类的源码剖析点的判别。在判别等候行列中有正在等候的线程之后,其会铲除头结点的状况信息,而且调用unparkSuccessor(Node)办法唤醒头结点的下一个节点,使其持续往下履行。如下是unparkSuccessor(Node)办法的详细完结:

            能够看到,unparkSuccessor(Node)办法的作用是唤醒离传入节点最近的一个处于等候状况的线程,使其持续往下履行。前面咱们讲到过,等候行列中的线程或许有多个,而调用countDown()办法的线程只唤醒了一个处于等候状况的线程,这儿剩余的等候线程是怎么被唤醒的呢?其实这些线程是被当时唤醒的线程唤醒的。详细的咱们能够看看await()办法的详细履行进程。如下是await()办法的代码:

            public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
            }

            await()办法实践仍是调用了Sync目标的办法acquireSharedInterruptibly(int)办法,如下是该办法的详细完并发编程中CountDownLatch类的源码剖析结:

            能够看到acquireSharedInterruptibly(int)办法判别当时线程是否需求以同享状况获取履行权限,这儿tryAcquireShared(int)办法是AbstractQueuedSynchronizer中的一个模板办法,其详细完结在前面的Sync类中,能够看到,其首要是判别state是否为零,假如为零则回来1,表明当时线程不需求进行权限获取,可直接履行后续代码,回来-1则表明当时线程需求进行同享权限。详细的获取履行权限的代码在doAcquireSharedInterruptibl并发编程中CountDownLatch类的源码剖析y(int)办法中,如下是该办法的详细完结:

            在doAcquireSharedInterruptibly(int)办法中,首要运用当时线程创立一个同享形式的节点。然后在一个for循环中判别当时线程是否获取到履行权限,假如有(r >= 0判别)则将当时节点设置为头节点,而且唤醒后续处于同享形式的节点;假如没有,则对调用shouldParkAfterFailedAcquire(Node, Node)和parkAndCheckInterrupt()办法使当时线程处于“放置”状况,该“放置”状况是由操作系统进行的,这样能够防止该线程无限循环而获取不到履行权限,形成资源糟蹋,这儿也便是线程处于等候状况的方位,也便是说当线程被堵塞的时分便是堵塞在这个方位。当有多个线程调用await()办法而进入等候状况时,这几个线程都将等候在此处。这儿回过头来看前面将的countDown()办法,其会唤醒处于等候行列中离头节点最近的一个处于等候状况的线程,也便是说该线程被唤醒之后会持续从这个方位开端往下履行,此刻履行到tryAcquireShared(int)办法时,发现r大于0(由于state现已被置为0了),该线程就会调用setHeadAndPropagate(Node, int)办法,而且退出当时循环,也就开端履行awat()办法之后的代码。这儿咱们看看setHeadAndPropagate(Node, int)办法的详细完结:

            setHeadAndPropagate(Node, int)办法首要作用是设置当时节点为头结点,而且将唤醒作业往下传递,在传递的进程中,其会判别被传递的节点是否是以同享形式测验获取履行权限的,假如不是,则传递到该节点处停止(一般状况下,等候行列中都只会都是处于同享形式或许处于独占形式的节点)。也便是说,头结点会顺次唤醒后续处于同享状况的节点,这也便是同享锁与独占锁的完结办法。这儿doReleaseShared()办法也便是咱们前面讲到的会将离头结点最近的一个处于等候状况的节点唤醒的办法。

            请关注微信公众号
            微信二维码
            不容错过
            Powered By Z-BlogPHP