Java系列課----高性能編程專題----線程通信(二)

承上啓下

上篇博客介紹了現在JDK棄用的api,這篇博客介紹現在使用的兩種api

wait/notify機制

這些方法只能由(同一對象鎖)的持有者線程調用,也就是卸載同步塊裏面,否則會拋出IllegalMonitorStateException異常。一定要在同步代碼段
wait方法導致當前線程等待,加入該對象的等待集合中,並且放棄當前持有的對象鎖,notify/notifyAll方法喚醒一個或所有的正在等待這個對象鎖的線程

package demo2;

public class wait {
    public static Object baozidian=null;
    public void waitNotifyTest() throws Exception{
        //啓動線程
        new Thread(()->{
            if(baozidian==null){    //如果沒有包子,則進入等待
                synchronized (this){
                    try{
                        System.out.println("1.進入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2.買到包子,回家");
        }).start();
        //3秒之後,生產一個包子
        Thread.sleep(3000L);
        baozidian=new Object();
        synchronized (this){
            this.notifyAll();
            System.out.println("3.通知消費者");
        }
    }
    public static void main(String[] args) throws Exception {
        new wait().waitNotifyTest();
    }
}

在這裏插入圖片描述

產生死鎖情況

注意,雖然會wait自動解鎖,但是對順序有要求,如果在notify被調用之後,纔開始wait方法的調用,線程會永遠處理waiting狀態

package demo2;

public class wait {
    public static Object baozidian=null;
    public void waitNotifyTest() throws Exception{
        //啓動線程
        new Thread(()->{
            if(baozidian==null){    //如果沒有包子,則進入等待
                try{
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this){
                    try{
                        System.out.println("1.進入等待");
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2.買到包子,回家");
        }).start();
        //3秒之後,生產一個包子
        Thread.sleep(3000L);
        baozidian=new Object();
        synchronized (this){
            this.notifyAll();       //喚醒所有的線程
            System.out.println("3.通知消費者");
        }
    }
    public static void main(String[] args) throws Exception {
        new wait().waitNotifyTest();
    }
}


在這裏插入圖片描述

3秒之後在notify被調用,不過這時候wait方法沒有被調用,5秒之後在進行被調用,所以線程會永遠處理waiting狀態

park/unpark機制

線程調用park則等待"許可",unpark方法爲指定線程提供"許可(permit)"
不要求park和unpark的調用順序
多次調用unpark之後,在調用park,線程會直接運行,但不會疊加,也就是說,連續多次調用park方法,第一次會拿到"許可"直接運行,後續調用會進入等待

package demo2;

import java.util.concurrent.locks.LockSupport;

public class park {

    public static Object baozidian=null;
    public void parkUnparkTest() throws Exception{
        try{                                //喚醒的程序已經被執行過了
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //啓動線程
        Thread consumerThread=new Thread(()->{
            if(baozidian==null){
                System.out.println("1.進入等待");
                LockSupport.park();
            }
            System.out.println("2.買到包子,回家");
        });
        consumerThread.start();
        //3秒之後,生產一個包子
        Thread.sleep(3000L);
        baozidian=new Object();
        LockSupport.unpark(consumerThread);
        System.out.println("3.通知消費者");
    }
    public static void main(String[] args) throws Exception {
        new park().parkUnparkTest();
    }

}

在這裏插入圖片描述

產生死鎖情況

不會對鎖進行釋放,只是操作線程

package demo2;

import java.util.concurrent.locks.LockSupport;

public class park {

    public static Object baozidian=null;
    public void parkUnparkTest() throws Exception{
        try{                                //喚醒的程序已經被執行過了
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //啓動線程
        Thread consumerThread=new Thread(()->{
            if(baozidian==null){
                System.out.println("1.進入等待");
                // 當前線程拿到鎖,然後掛起
                synchronized (this){		//沒有被釋放,一直處理被掛起的狀態
                    LockSupport.park();	
                }
            }
            System.out.println("2.買到包子,回家");
        });
        consumerThread.start();
        //3秒之後,生產一個包子
        Thread.sleep(3000L);
        baozidian=new Object();
        //爭取到鎖以後,在恢復consumerThread
        synchronized (this){
            LockSupport.unpark(consumerThread);
        }
        System.out.println("3.通知消費者");
    }
    public static void main(String[] args) throws Exception {
        new park().parkUnparkTest();
    }

}

在這裏插入圖片描述

警告!之前代碼中用if語句來判斷,是否進行等待狀態,是錯誤的
官方建議應該在while循環中檢查等待條件,原因是處於等待狀態的線程可能會收到錯誤警報和僞喚醒,如果不在循環中檢查等待條件,程序就會在沒有滿足結束條件的情況下推出。僞喚醒是指線程並非因爲notify,notifyall,unpark等api調用而喚醒,是更底層原因導致的

今天就先分享到這裏,之後還有一系列的課

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章