一、wait()、notify()、notifyAll()用法
obj.wait()/obj.wait(long timeout)是Object中的方法,當(dāng)線(xiàn)程調(diào)用wait()方法,當(dāng)前線(xiàn)程釋放對(duì)象鎖,進(jìn)入等待隊(duì)列。
obj.notify()/obj.nogifyAll()是Object中的方法,喚醒在此對(duì)象上wait()的單個(gè)或者所有線(xiàn)程。
測(cè)試代碼:
public class ThreadWaitNotify {
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建一個(gè)線(xiàn)程池
ExecutorService executorService = Executors.newCachedThreadPool();
//創(chuàng)建DemoTest對(duì)象
DemoTest demoTest = new DemoTest();
//用線(xiàn)程池創(chuàng)建線(xiàn)程異步執(zhí)行waitTest方法
executorService.submit(() -> demoTest.waitTest());
//用線(xiàn)程池創(chuàng)建線(xiàn)程異步執(zhí)行notifyTest方法
executorService.submit(() -> demoTest.notifyTest());
}
//測(cè)試wait和notify測(cè)試demo
static class DemoTest {
//喚醒線(xiàn)程
public synchronized void notifyTest() {
System.out.println("方法notifyTest開(kāi)始執(zhí)行了");
notify();
System.out.println("方法notifyTest執(zhí)行結(jié)束了");
}
//執(zhí)行wait操作將線(xiàn)程掛起,注意必須結(jié)合synchronized使用
public synchronized void waitTest() {
System.out.println("方法waitTest開(kāi)始執(zhí)行了");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法waitTest執(zhí)行結(jié)束了");
}
}
}
打印日志:
方法waitTest開(kāi)始執(zhí)行了
方法notifyTest開(kāi)始執(zhí)行了
方法notifyTest執(zhí)行結(jié)束了
方法waitTest執(zhí)行結(jié)束了
從日志中我們可以看出waitTest方法阻塞直到被notifyTest喚醒
二、await()、signal()、signalAll()用法
java.util.concurrent類(lèi)庫(kù)中提供的Condition類(lèi)來(lái)實(shí)現(xiàn)線(xiàn)程之間的協(xié)調(diào)。
condition.await()/condition.await(long time, TimeUnit unit):通過(guò)condition調(diào)用,當(dāng)前線(xiàn)程釋放對(duì)象鎖。
condition.signal()/condition.signalAll():通過(guò)signal或者signalAll方法喚醒a(bǔ)wait掛起的線(xiàn)程。
測(cè)試代碼:
public class ThreadAwaitSignal {
public static void main(String[] args) {
//創(chuàng)建一個(gè)線(xiàn)程池
ExecutorService executorService = Executors.newCachedThreadPool();
//創(chuàng)建DemoTest對(duì)象
DemoTest demoTest = new DemoTest();
//用線(xiàn)程池創(chuàng)建線(xiàn)程異步執(zhí)行awaitTest方法
executorService.submit(() -> demoTest.awaitTest());
//用線(xiàn)程池創(chuàng)建線(xiàn)程異步執(zhí)行signalTest方法
executorService.submit(() -> demoTest.signalTest());
}
/**
* 使用java.util.conncurrent類(lèi)中提供了Condition類(lèi)來(lái)實(shí)現(xiàn)線(xiàn)程之間的協(xié)調(diào)
* 可以在Condition上調(diào)用await()方法使線(xiàn)程掛起
* 其他線(xiàn)程調(diào)用signal()或者signalAll()來(lái)喚醒線(xiàn)程
*/
static class DemoTest {
//定義一個(gè)Lock對(duì)象用來(lái)獲取Condition對(duì)象
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//喚醒線(xiàn)程
public void signalTest() {
lock.lock();
try {
System.out.println("方法signalTest開(kāi)始執(zhí)行了");
condition.signalAll();
System.out.println("方法signalTest執(zhí)行結(jié)束了");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//結(jié)合lock鎖實(shí)現(xiàn)Condition的await
public void awaitTest() {
lock.lock();
try {
System.out.println("方法awaitTest開(kāi)始執(zhí)行了");
condition.await();
System.out.println("方法awaitTest執(zhí)行結(jié)束了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
打印日志:
方法awaitTest開(kāi)始執(zhí)行了
方法signalTest開(kāi)始執(zhí)行了
方法signalTest執(zhí)行結(jié)束了
方法awaitTest執(zhí)行結(jié)束了
從日志中國(guó)可以看出我們得到了和wait同樣的效果。
三 、yield()、join()用法
Thread.yield():一定是當(dāng)前線(xiàn)程調(diào)用此方法,當(dāng)前線(xiàn)程放棄獲取CPU的時(shí)間片,由運(yùn)行態(tài)轉(zhuǎn)變?yōu)榫途w態(tài),讓操作系統(tǒng)中再次選擇線(xiàn)程執(zhí)行。作用:讓相同優(yōu)先級(jí)的線(xiàn)程輪流執(zhí)行,但并不能保證輪流執(zhí)行,根據(jù)解釋我們了解到,轉(zhuǎn)成就緒態(tài)的的線(xiàn)程還有可能再次選中執(zhí)行。Thread.yield()方法不會(huì)導(dǎo)致阻塞。
t.join()/t.join(long millis):當(dāng)前線(xiàn)程調(diào)用t2.join()方法,當(dāng)前線(xiàn)程阻塞但是不會(huì)釋放對(duì)象鎖,直到t2線(xiàn)程執(zhí)行完畢或者millis時(shí)間到,則當(dāng)前的線(xiàn)程恢復(fù)就緒狀態(tài)。作用:讓優(yōu)先級(jí)比較高的線(xiàn)程優(yōu)先執(zhí)行。
yield測(cè)試代碼:
//yield放棄CPU時(shí)間片
public static void yieldTest(){
//定義一個(gè)線(xiàn)程
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 測(cè)試線(xiàn)程開(kāi)始執(zhí)行。。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 測(cè)試線(xiàn)程執(zhí)行結(jié)束了。");
});
thread.start();
System.out.println(Thread.currentThread().getName() + ": 執(zhí)行yield方法");
Thread.yield();
System.out.println(Thread.currentThread().getName() + ": 主線(xiàn)程開(kāi)始執(zhí)行");
}
打印結(jié)果:
main: 執(zhí)行yield方法
main: 主線(xiàn)程開(kāi)始執(zhí)行
Thread-0: 測(cè)試線(xiàn)程開(kāi)始執(zhí)行。。。
Thread-0: 測(cè)試線(xiàn)程執(zhí)行結(jié)束了。
可以看出雖然主線(xiàn)程調(diào)用了yield,但是仍然又開(kāi)始執(zhí)行了,因此但并不能保證輪流執(zhí)行。
join測(cè)試代碼:
//join搶占CPU時(shí)間片
public static void joinTest() throws InterruptedException {
//定義一個(gè)線(xiàn)程
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 測(cè)試線(xiàn)程開(kāi)始執(zhí)行。。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 測(cè)試線(xiàn)程執(zhí)行結(jié)束了。");
});
thread.start();
System.out.println(thread.getName() + ": 執(zhí)行join方法");
thread.join();
System.out.println(Thread.currentThread().getName() + ": 主線(xiàn)程開(kāi)始執(zhí)行");
}
打印日志:
Thread-0: 執(zhí)行join方法
Thread-0: 測(cè)試線(xiàn)程開(kāi)始執(zhí)行。。。
Thread-0: 測(cè)試線(xiàn)程執(zhí)行結(jié)束了。
main: 主線(xiàn)程開(kāi)始執(zhí)行
從日志中我們可以看出主線(xiàn)程在線(xiàn)程執(zhí)行完成后才開(kāi)始執(zhí)行。
四、wait()、await()、sleep()、yield、join對(duì)比
通過(guò)表格對(duì)比(join的情況下,t1指代當(dāng)前線(xiàn)程,t2代表其他線(xiàn)程)
