前言:
- sleep(1000) 是 Thread 中的方法,參數是sleep多少毫秒。sleep() 的作用是將當前線程暫停一段時間,但這期間不會釋放鎖
- wait、notify、notifyAll 是 Object 中的方法,可以作用於任何對象,用於控制線程的狀態,通常配合 synchronized 代碼塊使用
Sleep():
- 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三個方法之前,需要先了解兩個概念:等待池、鎖池;
- 等待池:當對象鎖【lock】 調用 wait 方法時, 持有該對象鎖的線程會進入到等待池中,並且釋放當前對象鎖,線程進入到被動狀態,需要notify notifyAll 將其喚醒。
- 鎖池: 假設某對象鎖當前某線程獲取,其他線程想要持有該對象鎖時,需要進入到鎖池中,等待對象鎖被釋放再去競爭鎖,線程進入到主動狀態,等待對象鎖空閒。
wait、notify、notifyAll簡介
- wait、notify、notifyAll 需要配合 synchronized (lock) 來使用,因爲對對象鎖的進行操作之前需要,對其先進行監聽操作。
- wait() : 會將當前線程進入休眠狀態,並且釋放當前獲取到的對象鎖,並將當前線程放入到等待池中,等待notify、notifyAll 將其喚醒並進入鎖池中競爭對象鎖,
- notify 從等待池中隨機選擇【和優先級也有關】喚醒其中一個線程,進入到鎖池中等待對象鎖,並執行,其他等待池中線程繼續等待。
- 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(),將線程二喚醒進入到鎖池中,線程一繼續等待被喚醒。
- 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 鎖之後全部執行完畢。