一昵慌、wait()假夺、notify()、notifyAll()用法
obj.wait()/obj.wait(long timeout)是Object中的方法斋攀,當線程調(diào)用wait()方法已卷,當前線程釋放對象鎖,進入等待隊列淳蔼。
obj.notify()/obj.nogifyAll()是Object中的方法侧蘸,喚醒在此對象上wait()的單個或者所有線程。
測試代碼:
public class ThreadWaitNotify {
public static void main(String[] args) throws InterruptedException {
//創(chuàng)建一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
//創(chuàng)建DemoTest對象
DemoTest demoTest = new DemoTest();
//用線程池創(chuàng)建線程異步執(zhí)行waitTest方法
executorService.submit(() -> demoTest.waitTest());
//用線程池創(chuàng)建線程異步執(zhí)行notifyTest方法
executorService.submit(() -> demoTest.notifyTest());
}
//測試wait和notify測試demo
static class DemoTest {
//喚醒線程
public synchronized void notifyTest() {
System.out.println("方法notifyTest開始執(zhí)行了");
notify();
System.out.println("方法notifyTest執(zhí)行結(jié)束了");
}
//執(zhí)行wait操作將線程掛起鹉梨,注意必須結(jié)合synchronized使用
public synchronized void waitTest() {
System.out.println("方法waitTest開始執(zhí)行了");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法waitTest執(zhí)行結(jié)束了");
}
}
}
打印日志:
方法waitTest開始執(zhí)行了
方法notifyTest開始執(zhí)行了
方法notifyTest執(zhí)行結(jié)束了
方法waitTest執(zhí)行結(jié)束了
從日志中我們可以看出waitTest方法阻塞直到被notifyTest喚醒
二讳癌、await()、signal()存皂、signalAll()用法
java.util.concurrent類庫中提供的Condition類來實現(xiàn)線程之間的協(xié)調(diào)晌坤。
condition.await()/condition.await(long time, TimeUnit unit):通過condition調(diào)用,當前線程釋放對象鎖旦袋。
condition.signal()/condition.signalAll():通過signal或者signalAll方法喚醒await掛起的線程骤菠。
測試代碼:
public class ThreadAwaitSignal {
public static void main(String[] args) {
//創(chuàng)建一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
//創(chuàng)建DemoTest對象
DemoTest demoTest = new DemoTest();
//用線程池創(chuàng)建線程異步執(zhí)行awaitTest方法
executorService.submit(() -> demoTest.awaitTest());
//用線程池創(chuàng)建線程異步執(zhí)行signalTest方法
executorService.submit(() -> demoTest.signalTest());
}
/**
* 使用java.util.conncurrent類中提供了Condition類來實現(xiàn)線程之間的協(xié)調(diào)
* 可以在Condition上調(diào)用await()方法使線程掛起
* 其他線程調(diào)用signal()或者signalAll()來喚醒線程
*/
static class DemoTest {
//定義一個Lock對象用來獲取Condition對象
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//喚醒線程
public void signalTest() {
lock.lock();
try {
System.out.println("方法signalTest開始執(zhí)行了");
condition.signalAll();
System.out.println("方法signalTest執(zhí)行結(jié)束了");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//結(jié)合lock鎖實現(xiàn)Condition的await
public void awaitTest() {
lock.lock();
try {
System.out.println("方法awaitTest開始執(zhí)行了");
condition.await();
System.out.println("方法awaitTest執(zhí)行結(jié)束了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
打印日志:
方法awaitTest開始執(zhí)行了
方法signalTest開始執(zhí)行了
方法signalTest執(zhí)行結(jié)束了
方法awaitTest執(zhí)行結(jié)束了
從日志中國可以看出我們得到了和wait同樣的效果。
三 疤孕、yield()商乎、join()用法
Thread.yield():一定是當前線程調(diào)用此方法,當前線程放棄獲取CPU的時間片胰柑,由運行態(tài)轉(zhuǎn)變?yōu)榫途w態(tài)截亦,讓操作系統(tǒng)中再次選擇線程執(zhí)行爬泥。作用:讓相同優(yōu)先級的線程輪流執(zhí)行柬讨,但并不能保證輪流執(zhí)行,根據(jù)解釋我們了解到袍啡,轉(zhuǎn)成就緒態(tài)的的線程還有可能再次選中執(zhí)行踩官。Thread.yield()方法不會導致阻塞。
t.join()/t.join(long millis):當前線程調(diào)用t2.join()方法境输,當前線程阻塞但是不會釋放對象鎖蔗牡,直到t2線程執(zhí)行完畢或者millis時間到,則當前的線程恢復就緒狀態(tài)嗅剖。作用:讓優(yōu)先級比較高的線程優(yōu)先執(zhí)行辩越。
yield測試代碼:
//yield放棄CPU時間片
public static void yieldTest(){
//定義一個線程
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 測試線程開始執(zhí)行。信粮。黔攒。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 測試線程執(zhí)行結(jié)束了。");
});
thread.start();
System.out.println(Thread.currentThread().getName() + ": 執(zhí)行yield方法");
Thread.yield();
System.out.println(Thread.currentThread().getName() + ": 主線程開始執(zhí)行");
}
打印結(jié)果:
main: 執(zhí)行yield方法
main: 主線程開始執(zhí)行
Thread-0: 測試線程開始執(zhí)行。督惰。不傅。
Thread-0: 測試線程執(zhí)行結(jié)束了。
可以看出雖然主線程調(diào)用了yield赏胚,但是仍然又開始執(zhí)行了访娶,因此但并不能保證輪流執(zhí)行。
join測試代碼:
//join搶占CPU時間片
public static void joinTest() throws InterruptedException {
//定義一個線程
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 測試線程開始執(zhí)行觉阅。崖疤。。");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 測試線程執(zhí)行結(jié)束了典勇。");
});
thread.start();
System.out.println(thread.getName() + ": 執(zhí)行join方法");
thread.join();
System.out.println(Thread.currentThread().getName() + ": 主線程開始執(zhí)行");
}
打印日志:
Thread-0: 執(zhí)行join方法
Thread-0: 測試線程開始執(zhí)行戳晌。。痴柔。
Thread-0: 測試線程執(zhí)行結(jié)束了沦偎。
main: 主線程開始執(zhí)行
從日志中我們可以看出主線程在線程執(zhí)行完成后才開始執(zhí)行。
四咳蔚、wait()豪嚎、await()、sleep()谈火、yield侈询、join對比
通過表格對比(join的情況下,t1指代當前線程糯耍,t2代表其他線程)