JUC同步工具CountDownLatch
阿新 • • 發佈:2022-12-12
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方法可以自由的對任務片段進行控制。