常見的線程之間通訊方式有以下幾種:java
一、wait和notify/notifyAll併發
二、await和signal/signalAllide
三、sleep/yield/join工具
四、同步屏障CyclicBarrier學習
五、CountDownLatch 閉鎖spa
六、Semaphore 信號量.net
注意:四、五、6參見後續章節線程
等待/通知機制,是指一個線程A調用了對象O的wait方法進入等待狀態,而另一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()方法返回,進而執行後續操做。上述兩個線程經過對象O完成交互,而對象上的wait()和notify()/notifyAll()的關係就如同開關信號同樣,用來完成等待方和通知方之間的交互工做。code
建立兩個線程WaitThread和NotifyThread,前者檢查flag值是否爲false,若是符合要求,進行後續操做,不然在lock上等待,後者在睡眠了一段時間值對lock進行通知,示例代碼以下:orm
package com.black.example.helloworld.thread; import ch.qos.logback.core.util.TimeUtil; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; /** * 等待/通知 超時設置模式 */ public class WaitNotify { static boolean flag = true; static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread waitThread =new Thread(new WaitDemo(),"WaitThread"); waitThread.start(); TimeUnit.SECONDS.sleep(1); Thread notifyThread =new Thread(new NotifyDemo(),"NotifyThread"); notifyThread.start(); } static class WaitDemo implements Runnable{ @Override public void run() { //加鎖,擁有lock的monitor synchronized (lock){ //條件不知足時,繼續wait,同時釋放lock的鎖 while (flag){ try { System.out.println(Thread.currentThread()+"flag is true, wait @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //條件知足時,完成工做 System.out.println(Thread.currentThread() + " flag is false,running @ "+ new SimpleDateFormat("HH:mm:ss").format(new Date())); } } } static class NotifyDemo implements Runnable{ @Override public void run() { try { //加鎖,擁有lock對象的Monitor synchronized (lock){ //獲取lock的鎖,而後進行通知,通知時不會釋放lock的鎖 //直到當前線程釋放了lock後,WaitThread才能從wait方法中返回 System.out.println(Thread.currentThread()+" hold lock, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.notifyAll(); flag = false; TimeUnit.SECONDS.sleep(5); } //再次加鎖 synchronized (lock){ System.out.println(Thread.currentThread()+" hold lock again, notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); TimeUnit.SECONDS.sleep(5); } } catch (InterruptedException e) { e.printStackTrace(); } } } }運行結果以下:
Condition定義了等待/通知兩種類型的方法,當前線程調用這些方法時,須要提早獲取Condition對象關聯的鎖。Condition對象是由Lock對象(調用Lock對象的newCondition()方法)建立出來的,換句話說,Condition是依賴Lock對象的。
通常都會講Condition對象做爲成員變量。當調用await()方法後,當前線程會是否鎖並進入等待,並且其餘線程調用signal()方法,通知當前線程後,當前線程才從await()方法返回,而且在返回前已經獲取到了鎖。
經常使用方法及描述,見下表:
await() 等待 與 singnal()通知的使用,示例以下:
package com.black.example.mutileThread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Condition 配合Lock 實現線程的等待 與通知 * Created by liuzp on 2018/7/24. */ public class ConditionDemo { public static Lock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); public static void main(String[] args) { new Thread() { @Override public void run() { lock.lock();//請求鎖 Thread.currentThread().setName("await-thread-lzp"); try { System.out.println(Thread.currentThread().getName() + "==》進入等待狀態,直到被通知"); condition.await();//設置當前線程進入等待 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//釋放鎖 } System.out.println(Thread.currentThread().getName() + "==》繼續執行"); } }.start(); new Thread() { @Override public void run() { lock.lock();//請求鎖 Thread.currentThread().setName("signal-thread-lzp"); try { System.out.println(Thread.currentThread().getName() + "=》進入喚醒一個Condition上的線程"); Thread.sleep(2000);//休息2秒 condition.signal();//隨機喚醒等待隊列中的一個線程 System.out.println(Thread.currentThread().getName() + "休息結束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//釋放鎖 } } }.start(); } }運行結果:
- 對於sleep()方法應該很熟悉了,讓當前線程睡眠一段時間。期間不會釋聽任何持有的鎖。
- yield()方法其做用主要是讓當前線程從運行狀態轉變爲就緒狀態,由線程調度從新選擇就緒狀態的線程分配CPU資源。至於最終會選取哪一個線程分配CPU資源就由調度策略來決定了,有可能仍是該線程,有可能換爲其它線程。
- join方法,做用是暫停當前線程,等待被調用線程指向結束以後再繼續執行。
使用join的時候須要注意:
一、調用join的時候,當前線程不會釋放掉鎖,若是調用線程也須要該鎖則就會致使死鎖!
二、join方法不會啓動調用線程,因此,在調用join以前,該調用線程必須已經start啓動,不然不會達到想要的效果。
join的底層實際是就是使用了一個自旋等待機制,判斷調用線程是否死亡,若是沒有則一直讓當前線程wait。能夠看一下底層實現源碼:public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) {//若是調用者依舊沒有結束,讓當前線程進行等待 wait(0);//注意這裏的wait是等待的當前線程,而不是調用者線程 } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay);//指定等待的時間 now = System.currentTimeMillis() - base; } } }
上一篇:學習筆記四:初識線程