Java多線程編程核心技術第三章小筆記:線程間通信

第一節:等待/通知機制

  1. 等待/通知機制出現的意義:減少CPU的資源浪費,而且還可以實現在多個線程間通信;
  2. 線程等待方法:Object類的wait()方法,將當前線程置入“預執行隊列”,必須在synchronized方法或者代碼塊中,如果不加入同步塊就會出現這個異常:java.lang.IllegalMonitorStateException;;執行後,當前線程釋放鎖,如果調用wait方法時沒有持有適當的鎖,拋出異常IllegalMonitorStateException,(RuntimeException)的一個子類,所以不需要try…catch語句進行捕獲異常;
  3. 線程喚醒:Object類的notify方法,如果沒有持有當前線程的鎖也會拋出異常IllegalMonitorStateException,
    執行該方法後,當前線程不會立即釋放該對象鎖,呈wait狀態的線程也並不會馬上獲取該對象鎖,要等待執行notify方法的線程將程序執行完,也就是退出同步塊之後線程纔會釋放鎖。
  4. 每個鎖對象都有兩個隊列,一個是就緒隊列,一個是阻塞隊列;就緒隊列存儲了將要獲得鎖的線程,阻塞隊列存儲了被阻塞的線程;
  5. 小總結: * notify方法執行完同步代碼塊就會釋放對象鎖 *
    執行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放; *
    執行了wait方法後,該線程會釋放對象鎖,並且進入線程等待池中,等待被喚醒;
  6. 當線程呈wait狀態時,調用線程對象的interrupt方法會出現InterruptedException異常
  7. notify()方法僅可以隨機喚醒一個線程;而notifyAll()方法可以喚醒全部等待中的線程;
  8. 方法wait(long):等待long時間內是否有線程對鎖喚醒,如果超過這個時間則自動喚醒;當然在這個時間內也可以由其他線程所喚醒;
  9. 兩個線程,若是A線程先調用了notify方法,然後B線程再調用wait方法,那麼B方法是不能被喚醒的,B方法會一直呈wait狀態;
  10. 生產者消費者模型:練習;
  11. 通過管道進行線程間通信;管道流(pipeStream),用於在不同線程間之間傳送數據。一個線程從輸入管道中讀取數據,通過使用管道,實現不同線程間的通信,從而無須藉助類似臨時文件之類的東西;

第二節:方法join的使用;

  1. join的由來:等待調用join這個方法的線程執行完畢後再進行下面的代碼;

  2. join的作用:使所屬線程對象x正常執行run方法裏的任務,而使當前線程z進行無限期的阻塞,等待線程x銷燬後再繼續執行線程z後面的代碼,;使線程排隊運行,有些類似同步的運行效果;

  3. join與synchronized的區別:join在內部使用wait方法進行等待,而synchronized關鍵字使用的是“對象監視器”原理作爲同步;

  4. 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的使用:

  1. ThreadLocal實現了每一個線程都有自己的共享變量,綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有變量;ThreadLocal類解決了變量在不同線程間的隔離性,也就是不同線程擁有自己的值,不同線程中的值可以放入ThreadLocal類中進行保存;
  2. ThreadLocal類的對象初始化默認值爲null;
  3. 解決第一次get返回null的問題: 在ThreadLocal類中有一個子類專用的方法:這個方法就是初始化值得,子類覆寫這個方法,return一個初始值;
protected T initialValue() {
    return null;
}
  1. 子線程和父線程同樣擁有各自得私有值;
  2. 類InheritableThreadLocal:使用該類可以讓子線程從父線程中取得值;
  3. 值繼承再修改:類InheritableThreadLocal提供的修改父線程值得方法;
protected T childValue(T parentValue) {
    return parentValue;
}
  • 此處注意一下,在使用InheritableThreadLocal類需要注意一點:如果子線程在取得值得同時,主線程將InheritableThreadLocal中得值進行修改,那麼子線程取得的值還是舊值;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章