第一節:等待/通知機制
- 等待/通知機制出現的意義:減少CPU的資源浪費,而且還可以實現在多個線程間通信;
- 線程等待方法:Object類的wait()方法,將當前線程置入“預執行隊列”,必須在synchronized方法或者代碼塊中,如果不加入同步塊就會出現這個異常:java.lang.IllegalMonitorStateException;;執行後,當前線程釋放鎖,如果調用wait方法時沒有持有適當的鎖,拋出異常IllegalMonitorStateException,(RuntimeException)的一個子類,所以不需要try…catch語句進行捕獲異常;
- 線程喚醒:Object類的notify方法,如果沒有持有當前線程的鎖也會拋出異常IllegalMonitorStateException,
執行該方法後,當前線程不會立即釋放該對象鎖,呈wait狀態的線程也並不會馬上獲取該對象鎖,要等待執行notify方法的線程將程序執行完,也就是退出同步塊之後線程纔會釋放鎖。 - 每個鎖對象都有兩個隊列,一個是就緒隊列,一個是阻塞隊列;就緒隊列存儲了將要獲得鎖的線程,阻塞隊列存儲了被阻塞的線程;
- 小總結: * notify方法執行完同步代碼塊就會釋放對象鎖 *
執行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放; *
執行了wait方法後,該線程會釋放對象鎖,並且進入線程等待池中,等待被喚醒; - 當線程呈wait狀態時,調用線程對象的interrupt方法會出現InterruptedException異常
- notify()方法僅可以隨機喚醒一個線程;而notifyAll()方法可以喚醒全部等待中的線程;
- 方法wait(long):等待long時間內是否有線程對鎖喚醒,如果超過這個時間則自動喚醒;當然在這個時間內也可以由其他線程所喚醒;
- 兩個線程,若是A線程先調用了notify方法,然後B線程再調用wait方法,那麼B方法是不能被喚醒的,B方法會一直呈wait狀態;
- 生產者消費者模型:練習;
- 通過管道進行線程間通信;管道流(pipeStream),用於在不同線程間之間傳送數據。一個線程從輸入管道中讀取數據,通過使用管道,實現不同線程間的通信,從而無須藉助類似臨時文件之類的東西;
第二節:方法join的使用;
-
join的由來:等待調用join這個方法的線程執行完畢後再進行下面的代碼;
-
join的作用:使所屬線程對象x正常執行run方法裏的任務,而使當前線程z進行無限期的阻塞,等待線程x銷燬後再繼續執行線程z後面的代碼,;使線程排隊運行,有些類似同步的運行效果;
-
join與synchronized的區別:join在內部使用wait方法進行等待,而synchronized關鍵字使用的是“對象監視器”原理作爲同步;
-
3、方法join遇到中斷時的情況,啓動的同時調用interrupt方法來中斷線程,
會拋出異常java.lang.InterruptedException
舉個例子:
/**
1. 觀察方法join與異常的發生
2. 如果當前線程對象被中斷,則當前線程出現異常
3. 調用interrupt方法之後
*/
class MyThread3A extends Thread{
@Override
public void run() {
for (int i = 0;i < 10;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("當前數字"+(i+1));
}
}
}
//定義兩個線程
class MyThread3B extends Thread{
@Override
public void run() {
try{
MyThread3A a = new MyThread3A();
a.start();
a.join();
System.out.println("線程B在最後打印了---");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class MyThread3C extends Thread{
private MyThread3B myThread3B;
public MyThread3C(MyThread3B myThread3B) {
this.myThread3B = myThread3B;
}
@Override
public void run() {
myThread3B.interrupt();
}
}
public class Test3 {
public static void main(String[] args) {
try{
MyThread3B myThread3B = new MyThread3B();
myThread3B.start();
Thread.sleep(500);
MyThread3C myThread3C = new MyThread3C(myThread3B);
myThread3C.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
運行結果:
出現異常的原因是線程B已經停止,但是線程A並未出現異常,A在線程B裏面調用了join方法,依舊是正常執行的狀態直到執行完畢;
4. 區別一下join(long)跟sleep(long)的區別
區別一下join(long)跟sleep(long)的區別:
第三節:類ThreadLocal的使用:
- ThreadLocal實現了每一個線程都有自己的共享變量,綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有變量;ThreadLocal類解決了變量在不同線程間的隔離性,也就是不同線程擁有自己的值,不同線程中的值可以放入ThreadLocal類中進行保存;
- ThreadLocal類的對象初始化默認值爲null;
- 解決第一次get返回null的問題: 在ThreadLocal類中有一個子類專用的方法:這個方法就是初始化值得,子類覆寫這個方法,return一個初始值;
protected T initialValue() {
return null;
}
- 子線程和父線程同樣擁有各自得私有值;
- 類InheritableThreadLocal:使用該類可以讓子線程從父線程中取得值;
- 值繼承再修改:類InheritableThreadLocal提供的修改父線程值得方法;
protected T childValue(T parentValue) {
return parentValue;
}
- 此處注意一下,在使用InheritableThreadLocal類需要注意一點:如果子線程在取得值得同時,主線程將InheritableThreadLocal中得值進行修改,那麼子線程取得的值還是舊值;