JUC同步工具CountDownLatch

2023-05-25,,

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方法可以自由的对任务片段进行控制。

JUC同步工具CountDownLatch的相关教程结束。

《JUC同步工具CountDownLatch.doc》

下载本文的Word格式文档,以方便收藏与打印。