多線程 Sleep、Wait、notify、notifyAll方法講解

前言:

  1. sleep(1000) 是 Thread 中的方法,參數是sleep多少毫秒。sleep() 的作用是將當前線程暫停一段時間,但這期間不會釋放鎖
  2. wait、notify、notifyAll 是 Object 中的方法,可以作用於任何對象,用於控制線程的狀態,通常配合 synchronized 代碼塊使用

Sleep():

  1. sleep 的作用是使當前線程睡眠指定的時間,放棄對CPU的佔用,但是不會放棄對線程以獲取到的鎖,例:
public class SleepTest extends Thread{

    // 鎖
    public static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        SleepTest sleepTest = new SleepTest();
        sleepTest.setName("test1");
        sleepTest.start();

        SleepTest sleepTest2 = new SleepTest();
        sleepTest2.setName("test2");
        sleepTest2.start();

    }

    @Override
    public void run() {
        System.out.println("ThreadName :" + Thread.currentThread().getName() + " start" );
        synchronized (lock){
            for (int i =0 ; i<5; i++){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ThreadName :" + Thread.currentThread().getName() + "; i == " + i);
            }
        }
    }
}

打印結果:

ThreadName :test2 start
ThreadName :test1 start
ThreadName :test2; i == 0
ThreadName :test2; i == 1
ThreadName :test2; i == 2
ThreadName :test2; i == 3
ThreadName :test2; i == 4
ThreadName :test1; i == 0
ThreadName :test1; i == 1
ThreadName :test1; i == 2
ThreadName :test1; i == 3
ThreadName :test1; i == 4

在main方法中我們,我們創建了兩個線程,它們共同競爭同一把鎖 lock ,根據打印結果可以看出,線程1 一直等到線程2 執行結束之後才執行 synchronized 中代碼塊, sleep(1000) 方法在線程2 中並沒有釋放 鎖lock ,直至執行結束

等待池、鎖池簡介:

瞭解這個wait、notify、notifyAll三個方法之前,需要先了解兩個概念:等待池、鎖池;

  1. 等待池:當對象鎖【lock】 調用 wait 方法時, 持有該對象鎖的線程會進入到等待池中,並且釋放當前對象鎖,線程進入到被動狀態,需要notify notifyAll 將其喚醒。
  2. 鎖池: 假設某對象鎖當前某線程獲取,其他線程想要持有該對象鎖時,需要進入到鎖池中,等待對象鎖被釋放再去競爭鎖,線程進入到主動狀態,等待對象鎖空閒。

wait、notify、notifyAll簡介

  1. wait、notify、notifyAll 需要配合 synchronized (lock) 來使用,因爲對對象鎖的進行操作之前需要,對其先進行監聽操作。
  2. wait() : 會將當前線程進入休眠狀態,並且釋放當前獲取到的對象鎖,並將當前線程放入到等待池中,等待notify、notifyAll 將其喚醒並進入鎖池中競爭對象鎖,
  3. notify 從等待池中隨機選擇【和優先級也有關】喚醒其中一個線程,進入到鎖池中等待對象鎖,並執行,其他等待池中線程繼續等待。
  4. notifyAll 將等待池中所有的線程全部喚醒到鎖池中,然後去競爭對象鎖,
    例 :
public class WaitTest extends Thread{

    // 鎖
    public static final Object lock = new Object();

    public static final Object lock1 = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 創建線程一
        WaitTest sleepTest = new WaitTest();
        sleepTest.setName("test1");
        sleepTest.start();

        // 創建線程二
        WaitTest sleepTest2 = new WaitTest();
        sleepTest2.setName("test2");
        sleepTest2.start();

        // sleep 保證線程一、二調用 wait 進入到等待池中
        Thread.sleep(1000);
        synchronized (lock){
            // 隨機喚醒一個線程進入到鎖池中等待對象鎖
            System.out.println("lock.notify");
            lock.notify();
        }

    }

    @Override
    public void run() {
        synchronized (lock){
            try {
                System.out.println("Thread-Name :" + Thread.currentThread().getName() + " wait ");
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i =0 ; i< 5; i++){
                System.out.println("Thread-Name :" + Thread.currentThread().getName() + "; i == " + i);
            }
        }
    }
}

打印結果:

Thread-Name :test2 wait 
Thread-Name :test1 wait 
lock.notify
Thread-Name :test2; i == 0
Thread-Name :test2; i == 1
Thread-Name :test2; i == 2
Thread-Name :test2; i == 3
Thread-Name :test2; i == 4

主線程sleep(1000) 期間,線程一二都通過 wait 進入到等待池中,在sleep() 之後,調用lock.notify(),將線程二喚醒進入到鎖池中,線程一繼續等待被喚醒。

  1. lock.notify(); 換成 lock.notifyAll(); 之後打印結果:
Thread-Name :test1 wait 
Thread-Name :test2 wait 
lock.notifyAll
Thread-Name :test2; i == 0
Thread-Name :test2; i == 1
Thread-Name :test2; i == 2
Thread-Name :test2; i == 3
Thread-Name :test2; i == 4
Thread-Name :test1; i == 0
Thread-Name :test1; i == 1
Thread-Name :test1; i == 2
Thread-Name :test1; i == 3
Thread-Name :test1; i == 4

根據結果可以看出,線程一二全部被喚醒,等待 lock 鎖之後全部執行完畢。

End.

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