Javaの並行処理の分析ツール-CountDownLatch

基本コンセプト

関数が共同で一緒に複数のスレッドを必要とし、その後、これらのスレッドが処理され、追従動作を継続するのを待つ必要があります:私たちは通常、あなたがこのような要求に遭遇する必要があり、開発しています。その後、我々はこのたCountDownLatch同時ツールキットを使用することもできます。

使用

package com.demo;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
    	// 参数5表示我们需要等待的线程数
        CountDownLatch cdl = new CountDownLatch(5);
        // 启动5个子线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    // ...
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    // 子线程完成以后,调用CountDownLatch.countDown()方法
                    System.out.println(Thread.currentThread().getName() + "执行完成");
                    cdl.countDown();
                }
            }, "线程-" + (i+1)).start();
        }
        // 调用调用await方法后,主线程阻塞,并等待所有子线程执行完成
        cdl.await();
        System.out.println("子线程全部执行完成:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}
复制代码

次のように実行結果は以下のとおりです。

线程-1执行完成
线程-4执行完成
线程-5执行完成
线程-2执行完成
线程-3执行完成
子线程全部执行完成:2020-03-12 12:42:41
复制代码

我々は、すべてのサブスレッドの実行が完了すると、メインスレッドが実行を続けることができ、ブロックされた状態にCountDownLatch.await()メソッドの皇帝のスレッドと呼ばれるメインChengkaiチー5つのサブスレッドを参照してください。

内部原則

私たちは、たCountDownLatchの内部を見て、ソースを追跡します。まず、最初にされたCountDownLatchコンストラクタに新しいオブジェクト:

public class CountDownLatch {
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // 新建一个同步器
        this.sync = new Sync(count);
    }
}
复制代码

ここでは、新たな同期オブジェクトを作成し、シンクは、私たちが見て行く、その内部クラスです。

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        Sync(int count) {
        	// 将同步状态设置为指定的值
            setState(count);
        }
        int getCount() {
            return getState();
        }
        // 省略一些代码
		...
    }
复制代码

私たちは、同期がAbstractQueuedSynchronizer、たCountDownLatchの記述が基づいてAQS(継承された参照AQS実装原理をここSETSTATE方法我々が通過するスレッドの数に割り当てられたばかりの同期状態状態に実装されています)。

public abstract class AbstractQueuedSynchronizer
	...
	// 将同步状态设置为5
    protected final void setState(int newState) {
        state = newState;
    }
}
复制代码

上記は、のawaitメソッドに何が行われたかの呼び出されたのawait下記をご覧新しいロジックたCountDownLatchオブジェクトです:

public class CountDownLatch {
 	public void await() throws InterruptedException {
        // 父类AbstractQueuedSynchronizer中实现
        sync.acquireSharedInterruptibly(1);
    }
}
复制代码

acquireSharedInterruptiblyこのメソッドは、親クラスAbstractQueuedSynchronizer同期に実装されています。

public abstract class AbstractQueuedSynchronizer
	...
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
         // 获取共享锁
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
	// 这边空实现,其它是交给子类去实现,我们这边是Sync
    protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();
    }
}
复制代码

私たちは、シンクに戻って、外観はこのメソッドの実装をtryAcquireShared:

    protected int tryAcquireShared(int acquires) {
   	   return (getState() == 0) ? 1 : -1;
    }
复制代码

ここでは簡単ですが、比較的同期ステータス状態は、0を返す1に抗等しい - 1から返されたので、ここで、我々は5初期設定になっ始まる-1を返します。中AbstractQueuedSynchronizerへ戻ります

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 这边返回确实小于0
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
复制代码

フロントtryAcquireSharedリターンであるため-1、条件はdoAcquireSharedInterruptibly方法に、側場合に満たされます。

public abstract class AbstractQueuedSynchronizer
	...
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 当前线程包装成node放入阻塞队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null;
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}
复制代码

ここでは、唯一のparkAndCheckInterrupt方法で懸念されています。

 private final boolean parkAndCheckInterrupt() {
 		// 阻塞当前线程
        LockSupport.park(this);
        return Thread.interrupted();
    }
复制代码

今までの分析までは、私たちは、メインスレッドが完了のawaitメソッドは、ここにアップされているだろう呼び出すことを知っています。だから、それはどのようにそれを起こされることですか?そして、我々はそれがCountDownLatch.countDownを達成するために追跡し続け、我々はこのアプローチに入りました。

public class CountDownLatch {
	...
    public void countDown() {
    	// 调用父类AbstractQueuedSynchronizer的方法
        sync.releaseShared(1);
    }
}
复制代码

またreleaseSharedこの方法は、AbstractQueuedSynchronizerで親クラスに実装されています。

public abstract class AbstractQueuedSynchronizer
    public final boolean releaseShared(int arg) {
    	// 模板方法,调用子类Sync的tryReleaseShared方法
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
}
复制代码

tryReleaseSharedはで同期に実装されています。

protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                // 同步状态已经是0了就直接返回false
                if (c == 0)
                    return false;
                int nextc = c-1;
                // 使用CAS操作:同步状态减1
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
复制代码

このコードのロジックはカウントダウン方式、同期ステータス状態が1を差し引きます一度呼ばれたときに同期状態の状態がゼロになった場合、我々は最後のサブスレッドの実行方法COUNTDOWNそれを終えたときに、それはそう、trueを返すということですこれは、trueを返します。私たちは、releaseShared方法に戻ります。

public abstract class AbstractQueuedSynchronizer
   public final boolean releaseShared(int arg) {
   		// 当最后一个线程执行完,tryReleaseShared返回true
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
 }
复制代码

私たちは、tryReleaseSharedがtrueを返し、最後のスレッドの実行が完了すると知って、それはdoReleaseShared方法を入力します。

   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;  
                    // 唤醒阻塞的线程
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;
            }
            if (h == head)
                break;
        }
    }
复制代码

ここでは、唯一のunparkSuccessor方法で懸念されています。

    private void unparkSuccessor(Node node) {
        ...// 省略一些代码
        if (s != null)
        	// 唤醒线程
            LockSupport.unpark(s.thread);
    }
复制代码

最終的に、我々は最後の子スレッドが終了すると、メインスレッドが起こされるだろうことがわかります。

おすすめ

転載: juejin.im/post/5e69d597e51d452717263f8f