代码改变世界

JUC同步工具CountDownLatch

2022-12-12 10:06  杭伟  阅读(17)  评论(0编辑  收藏  举报

CountDownLatch:允许一条或多条线程等待其它线程中的一组操作完成后再继续执行。

 

在探究CountDownLatch之前,我们知道Thread的join也有类似功能,先看thread的join方法:

 1 public static void main(String[] args) throws InterruptedException{
 2         Thread t1 = new Thread(new Runnable() {
 3             @Override
 4             public void run() {
 5                 System.out.println("i am t1");
 6             }
 7         });
 8         Thread t2 = new Thread(new Runnable() {
 9             @Override
10             public void run() {
11                 try {
12                     t1.join();
13                 }
14                 catch (InterruptedException e){}
15                 System.out.println("i am t2");
16             }
17         });
18         Thread t3 = new Thread(new Runnable() {
19             @Override
20             public void run() {
21                 try {
22                     t2.join();
23                 }
24                 catch (InterruptedException e){}
25                 System.out.println("i am t3");
26             }
27         });
28         t1.start();
29         t2.start();
30         t3.start();
31     }

 

 Thread join方法原理:

 

 源码中,可以看到join调用了Object的wait方法,是一个无限等待。

疑问:既然这里有wait,那notify去哪了? 

原来在jvm源码中有这样一句:

 1 // 位于/hotspot/src/share/vm/runtime/thread.cpp中
 2 void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
 3     // ...
 4 
 5     // Notify waiters on thread object. This has to be done after exit() is called
 6     // on the thread (if the thread is the last thread in a daemon ThreadGroup the
 7     // group should have the destroyed bit set before waiters are notified).
 8     // 看这里
 9     ensure_join(this);
10 
11     // ...
12 }
13 
14 
15 static void ensure_join(JavaThread* thread) {
16     // We do not need to grap the Threads_lock, since we are operating on ourself.
17     Handle threadObj(thread, thread->threadObj());
18     assert(threadObj.not_null(), "java thread object must exist");
19     ObjectLocker lock(threadObj, thread);
20     // Ignore pending exception (ThreadDeath), since we are exiting anyway
21     thread->clear_pending_exception();
22     // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
23     java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
24     // Clear the native thread instance - this makes isAlive return false and allows the join()
25     // to complete once we've done the notify_all below
26     java_lang_Thread::set_thread(threadObj(), NULL);
27 
28     // 看这里
29     lock.notify_all(thread);
30 
31     // Ignore pending exception (ThreadDeath), since we are exiting anyway
32     thread->clear_pending_exception();
33 }

 

回到CountDownLatch:

使用:

 1 public static void main(String[] args) throws InterruptedException{
 2         CountDownLatch c2 = new CountDownLatch(1);//t2使用
 3         CountDownLatch c3 = new CountDownLatch(1);//t3使用
 4         Thread t1 = new Thread(new Runnable() {
 5             @Override
 6             public void run() {
 7                 System.out.println("i am t1");
 8                 c2.countDown();
 9             }
10         });
11 
12         Thread t2 = new Thread(new Runnable() {
13             @Override
14             public void run() {
15                 try {
16                     c2.await();
17                 }
18                 catch (InterruptedException e){}
19                 System.out.println("i am t2");
20                 c3.countDown();
21             }
22         });
23         Thread t3 = new Thread(new Runnable() {
24             @Override
25             public void run() {
26                 try {
27                     c3.await();
28                 }
29                 catch (InterruptedException e){}
30                 System.out.println("i am t3");
31             }
32         });
33         t1.start();
34         t2.start();
35         t3.start();
36     }

CountDownLatch原理:

利用AQS,主线程(需要等待其它线程的线程)进入AQS队列中等待,前置线程通过State获取锁并执行任务,当State=0时即表示任务全部执行完毕。主线程唤醒。

await()方法 ==》

主线程入队等待:LockSupport.park()

 

countDown方法 ==》分成两步:

1,前置线程执行完释放锁,State-1;

2,一旦State=0,唤醒主线程。

 

 

可以看到,CountDownLatch更加灵活,Join方法必须等待前置线程执行完毕才能开始执行下一线程,中间过程无法控制;

而CountDownLatch的countDown方法可以自由的对任务片段进行控制。