線程篇2:[- sleep、wait、notify、join、yield -]

前言:線程的五種狀態

本文是線程篇的一個分支,主要結合個人理解,看一下sleep和wait以及線程的一些狀態
網上的圖看起來都有點醜,我本身畫了一幅:bash

線程狀態

1.New:       新建態:  new Thread ~ thread.start期間
2.Runnable:  可執行態: 可被CPU調度執行期間。
3.Running     運行態: 線程獲取CPU權限進行執行
4.Blocked     阻塞狀態: 阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。
    |---等待阻塞:經過調用線程的wait()方法,讓線程等待某工做的完成。
    |---同步阻塞:線程在獲取synchronized同步鎖失敗(由於鎖被其它線程所佔用)時
    |---其餘阻塞:經過調用線程的sleep()或join()或發出了I/O請求時
5.Dead        死亡狀態: 線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
複製代碼

1、Thread.sleep簡述

暫停當前線程,進入Blocked狀態,把cpu片斷讓出給其餘線程。ide

1.測試代碼:
public class Main0 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        new Thread(new Car()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Ambulance()).start();
    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
        }
    }

    private static class Ambulance implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車開始啓動,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車跑到終點");
        }
    }
}
複製代碼

2. 結果分析:注02:29表明當前時刻的分秒,即2分29秒

---->[運行結果]----------------------
02:29:小汽車開始啓動,在路上跑
02:29:小汽車跑到終點
02:31:救護車開始啓動,在路上跑
02:31:救護車跑到終點
複製代碼

2、線程內sleep以及synchronized

1.子線程加入休眠
private static class Car implements Runnable {
    @Override
    public void run() {
        System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
    }
}
複製代碼

2.結果分析

Car線程的任務是睡5s,可見主線程的休眠沒有影響到Car子線程的運行(休眠)post

18:48:小汽車開始啓動,在路上跑
18:50:救護車開始啓動,在路上跑
18:50:救護車跑到終點
18:53:小汽車跑到終點
複製代碼

3.當加鎖睡眠時

在線程1中加synchronized(這裏鎖用sdf對象,你也能夠任意)測試

public class Main2 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
    public static void main(String[] args) {
        new Thread(new Car()).start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Ambulance()).start();
    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            synchronized (sdf){
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
            }
        }
    }

    private static class Ambulance implements Runnable {
        @Override
        public void run() {
            synchronized (sdf){
                System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車開始啓動,在路上跑");
                System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車跑到終點");
            }

        }
    }
}
複製代碼

2.結果分析

Car線程和Ambulance線程在運行時使用同一把鎖,線程在休眠時不會釋放鎖
因此Ambulance線程須要等待Car線程執行完成,才能進行執行ui

23:46:小汽車開始啓動,在路上跑
23:51:小汽車跑到終點
23:51:救護車開始啓動,在路上跑
23:51:救護車跑到終點
複製代碼

3、Object#wait()方法的做用

線程t1調用A對象wait()方法,會釋放t1持有的鎖,讓t1進入等待隊列(Blocked狀態)
直到其餘線程調用A對象的notify()方法notifyAll()方法t1進入同步隊列(Blocked狀態)
當t1得到鎖後會進入就緒狀態Runnable,獲取CPU的調度權後會繼續執行,再貼一遍這個圖:spa

線程狀態


1.wait方法的使用

既然是釋放當前線程的鎖,那麼不須有鎖才行,並且必須用該鎖的對象調用wait方法
好比上面是用sdf對象加鎖的,必須使用sdf.wait();,不然會拋出InterruptedException線程

private static class Car implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
            try {
                Thread.sleep(3000);//模擬執行3s的任務以後
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            try {
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車緊急剎車....");
                sdf.wait();
                System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
        }
    }
}
private static class Ambulance implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車開始啓動,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車跑到終點");
        }
    }
}
複製代碼

2.結果分析

在Car線程調用sdf.wait();後,鎖將被釋放,而後Ambulance線程就能夠持有鎖運行了
若是不喚醒線程,線程將一直阻塞,就是根本停不下來。打個比方就是sdf是司機
wait()以後就把車鑰匙扔了,而後熄火了。鑰匙拿不回來,車就跑不了。須要notify()獲取車鑰匙3d

30:48:小汽車開始啓動,在路上跑
30:51:小汽車緊急剎車....
30:51:救護車開始啓動,在路上跑
30:51:救護車跑到終點
而後就阻塞在這裏停不下來了....
複製代碼

3.喚醒等待線程

注意wait和notify須要使用同一個對象,不然然並卵
在Ambulance線程跑完後喚醒Car線程,而後Car獲取所後會進入就緒態code

private static class Ambulance implements Runnable {
    @Override
    public void run() {
        synchronized (sdf) {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車開始啓動,在路上跑");
            System.out.println(sdf.format(System.currentTimeMillis()) + ":救護車跑到終點");
            sdf.notify();
        }
    }
}
複製代碼

4.結果分析

40:23:小汽車開始啓動,在路上跑
40:26:小汽車緊急剎車....
40:26:救護車開始啓動,在路上跑
40:26:救護車跑到終點
40:26:救護車:喂,哥們,醒醒,你能夠開了...
40:26:小汽車開始啓動....
40:31:小汽車跑到終點
複製代碼

4、Thread#join()方法 和Thread#yield

1.普通代碼
public class Main6 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        Thread car = new Thread(new Car());
        car.start();
        System.out.println(sdf.format(System.currentTimeMillis()) + ":main線程結束");

    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
            try {
                Thread.sleep(3000);//模擬執行3s的任務以後
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
        }
    }
}
複製代碼

2.結果分析

26:28:小汽車開始啓動,在路上跑
26:28:main線程結束
26:31:小汽車跑到終點
複製代碼

3.join方法的使用

阻塞當前線程,知道join的線程結束或超時orm

public class Main6 {
    static SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");

    public static void main(String[] args) {
        Thread car = new Thread(new Car());
        car.start();
        try {
            car.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(sdf.format(System.currentTimeMillis()) + ":main線程結束");

    }

    private static class Car implements Runnable {
        @Override
        public void run() {
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車開始啓動,在路上跑");
            try {
                Thread.sleep(3000);//模擬執行3s的任務以後
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(sdf.format(System.currentTimeMillis()) + ":小汽車跑到終點");
        }
    }
}
---->[打印結果]-----------------------------
31:53:小汽車開始啓動,在路上跑
31:56:小汽車跑到終點
31:56:main線程結束
複製代碼

4.yield方法測試

讓出線程的cpu調度權,以後再一塊兒搶奪。運行狀態-->就緒狀態

public class Main7 {
    public static void main(String[] args) {
        Thread car = new Car("car");
        Thread ambulance = new Car("Ambulance");
        car.start();
        ambulance.start();
    }

    private static class Car extends Thread {
        public Car(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(getName() + "----" + i);
                if (i == 5) {
                    yield();
                }
            }
        }
    }
}
複製代碼

運行了幾組數據,大部分知足讓出調度權後,另外一個線程執行,也有少許狀況不是。
因此yield說明我如今不急,能夠劃劃水,執行權可讓出去。不過接下來誰執行仍是不定的。


5、小結

1.須要補充的點:

1.關於synchronized鎖這裏不展開,不瞭解的能夠見:線程篇3:[-synchronized-]
2.關於synchronized鎖對象須要一致,不然鎖不住,然並卵。經常使用class對象,如Car.class
3.能夠設置超時喚醒xxx.wait(3000),即3s後喚醒線程。
4.能夠設置join超時xxx.join(3000),即3s後線程進入就緒態。
5.notifyAll:來着黑暗寒冬的隨從們,僕人們,士兵們,遵從克爾蘇加德的召喚吧!


2.簡單比較
item 從屬 是否釋放鎖 狀態轉變爲 異常
sleep Thread 靜態方法 NO 阻塞 InterruptedException
wait Object 公共方法 YES 等待隊列 IllegalMonitorStateException+InterruptedException
notify/notifyAll Object 公共方法 -- 同步隊列 IllegalMonitorStateException
join Thread 公共方法 NO 阻塞 InterruptedException
yield Thread 公共方法 NO 可執行態
相關文章
相關標籤/搜索