Java Thread多線程開(kāi)發(fā)中Object類詳細(xì)講解
方法概覽
Thread
wait notify notifyAll方法詳解
作用
阻塞階段
使用了wait方法之后,線程就會(huì)進(jìn)入阻塞階段,只有發(fā)生以下四種情況中的其中一個(gè),線程才會(huì)被喚醒
- 另一個(gè)線程調(diào)用了這個(gè)線程的notify方法,剛好喚醒的是本線程
- 另一個(gè)線程調(diào)用了這個(gè)對(duì)象的notifyAll方法
- 過(guò)了wait規(guī)定的超時(shí)時(shí)間
- 線程調(diào)用了interrupt
喚醒階段
notify會(huì)喚醒單個(gè)處于阻塞狀態(tài)的線程,喚醒的線程是隨機(jī)的
notify和wait都需要寫(xiě)在synchronized代碼塊里,不然會(huì)拋出異常
notifyAll會(huì)喚醒所有等待的線程
遇到中斷
執(zhí)行wait方法之后,被中斷,會(huì)拋出InterruptedException這個(gè)異常
代碼展示
- 展示wait和notify的基本用法
- 該代碼執(zhí)行wait方法之后會(huì)釋放鎖,然后thread2執(zhí)行notify方法
- notify方法執(zhí)行完畢之后,并沒(méi)有立即釋放鎖,而是接著執(zhí)行之后的代碼,也就是打印“Thread2調(diào)用notify”這句話
- thread2執(zhí)行完畢之后,會(huì)進(jìn)行釋放鎖,thread1才會(huì)繼續(xù)執(zhí)行
- 在此期間,thread1雖然被喚醒,但是一直在等待thread2同步代碼塊里面的代碼執(zhí)行完畢
public class Wait { public static void main(String[] args) throws InterruptedException { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); Thread.sleep(200); thread2.start(); } public static Object object = new Object(); static class Thread1 extends Thread { @Override public void run() { synchronized (object) { System.out.println("Thread1執(zhí)行"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread1獲取鎖"); } } } static class Thread2 extends Thread { @Override public void run() { synchronized (object) { object.notify(); System.out.println("Thread2調(diào)用notify"); } } } } /* Thread1執(zhí)行 Thread2調(diào)用notify Thread1獲取鎖 * */
- notify和notifyAll的展示
- 第一個(gè)輸出:threadc調(diào)用notifyAll
- 第二個(gè)輸出:threadc調(diào)用notify
- 調(diào)用notify的時(shí)候,程序并沒(méi)有結(jié)束,threadb陷入等待
public class notifyOrAll implements Runnable{ private static final Object a = new Object(); public static void main(String[] args) throws InterruptedException { Runnable r = new notifyOrAll(); Thread threada = new Thread(r); Thread threadb = new Thread(r); Thread threadc = new Thread(new Runnable() { @Override public void run() { synchronized (a) { // a.notifyAll(); a.notify(); System.out.println(Thread.currentThread().getName() + "notify"); } } }); threada.start(); Thread.sleep(200); threadb.start(); Thread.sleep(200); threadc.start(); } @Override public void run() { synchronized (a) { System.out.println(Thread.currentThread().getName() + "得到鎖"); try { System.out.println(Thread.currentThread().getName() + "wait"); a.wait(); System.out.println(Thread.currentThread().getName() + "wait結(jié)束"); } catch (InterruptedException e) { e.printStackTrace(); } } } } /* Thread-0得到鎖 Thread-0wait Thread-1得到鎖 Thread-1wait Thread-2notifyAll Thread-1wait結(jié)束 Thread-0wait結(jié)束 * */ /* Thread-0得到鎖 Thread-0wait Thread-1得到鎖 Thread-1wait Thread-2notify Thread-0wait結(jié)束 * */
- 只釋放當(dāng)前monitor
- 證明wait只釋放當(dāng)前的那把鎖
public class OwnMonitor { private static volatile Object a = new Object(); private static volatile Object b = new Object(); public static void main(String[] args) throws InterruptedException { Thread threadA = new Thread(new Runnable() { @Override public void run() { synchronized (a) { System.out.println("threadA得到a"); synchronized (b) { System.out.println("threadA得到鎖b"); try { System.out.println("threadA釋放a"); a.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { synchronized (a) { System.out.println("threadB得到a"); System.out.println("threadB要獲取b"); synchronized (b) { System.out.println("threadB得到b"); } } } }); threadA.start(); Thread.sleep(1000); threadB.start(); } } /* threadA得到a threadA得到鎖b threadA釋放a threadB得到a threadB要獲取b * */
特點(diǎn)
- 執(zhí)行這些方法必須先獲取鎖
- notify只能換取一個(gè),而且是隨機(jī)的
- 都屬于Object。任何對(duì)象都可以調(diào)用
- 都是native final修飾的
當(dāng)線程從wait狀態(tài)剛被喚醒時(shí),通常不能直接得到鎖,那就會(huì)從waiting狀態(tài)轉(zhuǎn)換到blocked狀態(tài),搶到鎖之后狀態(tài)轉(zhuǎn)變?yōu)閞unnable
如果發(fā)生異常,則直接跳到Terminated狀態(tài)
通過(guò)wait notify方法實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者
- 將storge當(dāng)作生產(chǎn)者和消費(fèi)者進(jìn)行工作的倉(cāng)庫(kù)
- 如果storge中沒(méi)有數(shù)據(jù),生產(chǎn)者就開(kāi)始wait
- 如果storge中數(shù)據(jù)滿了,消費(fèi)者就開(kāi)始wait
- 生產(chǎn)者和消費(fèi)者每進(jìn)行一次生產(chǎn)和消費(fèi),就執(zhí)行notify
public class ProducerConsumer { public static void main(String[] args) { Storge storge = new Storge(); Producer producer = new Producer(storge); Consumer consumer = new Consumer(storge); new Thread(producer).start(); new Thread(consumer).start(); } } class Producer implements Runnable { private Storge storge; public Producer(Storge storge) { this.storge = storge; } @Override public void run() { for (int i = 0; i < 100; i++) { storge.put(); } } } class Consumer implements Runnable { private Storge storge; public Consumer(Storge storge) { this.storge = storge; } @Override public void run() { for (int i = 0; i < 100; i++) { storge.take(); } } } class Storge { private int maxSize; private LinkedList<Date> storge; public Storge() { maxSize = 10; storge = new LinkedList<>(); } public synchronized void put() { while (storge.size() == maxSize) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storge.add(new Date()); System.out.println("已經(jīng)有了" + storge.size()); notify(); } public synchronized void take() { while (storge.size() == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("拿到了" + storge.poll() + "還剩" + storge.size()); notify(); } }
sleep方法詳解
作用:讓線程在預(yù)期的時(shí)間執(zhí)行,其他時(shí)間不占用CPU資源
特點(diǎn):和wait不一樣,sleep不釋放鎖
sleep不會(huì)釋放鎖
證明sleep不會(huì)釋放 synchronized鎖
public class SleepSyn implements Runnable{ public static void main(String[] args) { SleepSyn sleepSyn = new SleepSyn(); new Thread(sleepSyn).start(); new Thread(sleepSyn).start(); } @Override public void run() { syn(); } private synchronized void syn() { System.out.println(Thread.currentThread().getName() + "獲取鎖"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "釋放鎖"); } } /* * Thread-0獲取鎖 Thread-0釋放鎖 Thread-1獲取鎖 Thread-1釋放鎖 * */
證明sleep不釋放Lock鎖
public class sleepLock implements Runnable{ private static final Lock LOCK = new ReentrantLock(); @Override public void run() { LOCK.lock(); System.out.println(Thread.currentThread().getName() + "獲取鎖"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { LOCK.unlock(); } System.out.println(Thread.currentThread().getName() + "釋放鎖"); } public static void main(String[] args) { sleepLock sleepLock = new sleepLock(); new Thread(sleepLock).start(); new Thread(sleepLock).start(); } } /* * Thread-0獲取鎖 Thread-0釋放鎖 Thread-1獲取鎖 Thread-1釋放鎖 * */
sleep響應(yīng)中斷
- 拋出InterruptedException
- 會(huì)清除中斷狀態(tài)
- 中斷之后,拋出異常繼續(xù)執(zhí)行
public class sleepInterrupted implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new sleepInterrupted()); thread.start(); Thread.sleep(2000); thread.interrupt(); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(new Date()); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { System.out.println("中斷"); e.printStackTrace(); } } } } /* * Fri Jan 27 21:11:57 CST 2023 Fri Jan 27 21:11:58 CST 2023 中斷 Fri Jan 27 21:11:59 CST 2023 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.jx.JavaTest.ThreadObjectMethod.sleepInterrupted.run(sleepInterrupted.java:21) at java.lang.Thread.run(Thread.java:748) Fri Jan 27 21:12:00 CST 2023 Fri Jan 27 21:12:01 CST 2023 Fri Jan 27 21:12:02 CST 2023 Fri Jan 27 21:12:03 CST 2023 Fri Jan 27 21:12:04 CST 2023 Fri Jan 27 21:12:05 CST 2023 Fri Jan 27 21:12:06 CST 2023 Process finished with exit code 0 * */
總結(jié)
sleep方法可以讓線程進(jìn)入waiting狀態(tài),不占用CPU資源,但是不釋放鎖,規(guī)定時(shí)間之后再運(yùn)行
休眠期間如果被打斷,會(huì)拋出異常并清除中斷狀態(tài)
join方法詳解
新線程加入,主線程等子線程執(zhí)行完畢
代碼展示
- 前一個(gè)結(jié)果是使用join
- 后一個(gè)結(jié)果是沒(méi)使用join
- 可知使用join之后,主線程會(huì)等join的線程執(zhí)行完畢再繼續(xù)執(zhí)行
public class join { public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢"); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢"); } }); thread1.start(); thread2.start(); System.out.println("開(kāi)始等待子線程運(yùn)行"); // thread1.join(); // thread2.join(); System.out.println("所有線程執(zhí)行完畢"); } } /* * 開(kāi)始等待子線程運(yùn)行 Thread-0執(zhí)行完畢 Thread-1執(zhí)行完畢 所有線程執(zhí)行完畢 * */ /* * 開(kāi)始等待子線程運(yùn)行 所有線程執(zhí)行完畢 Thread-1執(zhí)行完畢 Thread-0執(zhí)行完畢 * */
- 遇到中斷
- 第一個(gè)的運(yùn)行結(jié)果是主線程沒(méi)中斷的打印結(jié)果
- 第二個(gè)的運(yùn)行結(jié)果是join期間進(jìn)行中斷的打印結(jié)果,可知在打印了“子線程運(yùn)行完畢”之后,依然打印了“啟動(dòng)”兩個(gè)字,可知會(huì)造成運(yùn)行混亂
- 可以在捕獲異常的代碼塊中,將join的線程也中斷,可以解決上面的問(wèn)題
public class joinInterrupt { public static void main(String[] args) { Thread main1 = Thread.currentThread(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { main1.interrupt(); Thread.sleep(2000); System.out.println("啟動(dòng)"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread1.start(); System.out.println("join"); try { thread1.join(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "中斷"); // thread1.interrupt(); e.printStackTrace(); } System.out.println("子線程運(yùn)行完畢"); } } /* * join 啟動(dòng) 子線程運(yùn)行完畢 * */ /* * join main中斷 子線程運(yùn)行完畢 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Thread.join(Thread.java:1252) at java.lang.Thread.join(Thread.java:1326) at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23) 啟動(dòng) Process finished with exit code 0 * */ /* * join main中斷 子線程運(yùn)行完畢 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt$1.run(joinInterrupt.java:13) at java.lang.Thread.run(Thread.java:748) java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Thread.join(Thread.java:1252) at java.lang.Thread.join(Thread.java:1326) at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23) Process finished with exit code 0 * */
join期間,線程處于WAITING狀態(tài)
public class joinStates { public static void main(String[] args) throws InterruptedException { Thread main1 = Thread.currentThread(); Thread thread = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); System.out.println(main1.getState()); System.out.println("子線程運(yùn)行結(jié)束"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); System.out.println("join"); thread.join(); System.out.println("運(yùn)行完畢"); } } /* * join WAITING 子線程運(yùn)行結(jié)束 運(yùn)行完畢 * */
yield方法
用來(lái)釋放CPU時(shí)間片,但是不一定能達(dá)到預(yù)期的效果,因?yàn)橛袝r(shí)CPU資源不緊張,無(wú)需yield
和sleep的區(qū)別是:sleep期間不會(huì)被再次調(diào)度但是yield會(huì)立刻處于競(jìng)爭(zhēng)狀態(tài),還會(huì)隨時(shí)再次被調(diào)度
到此這篇關(guān)于Java Thread多線程開(kāi)發(fā)中Object類詳細(xì)講解的文章就介紹到這了,更多相關(guān)Java Object類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
實(shí)例講解Java的設(shè)計(jì)模式編程中責(zé)任鏈模式的運(yùn)用
這篇文章主要介紹了Java的設(shè)計(jì)模式編程中責(zé)任鏈模式的運(yùn)用,講解了通過(guò)條件判斷結(jié)構(gòu)來(lái)分配不同對(duì)象的責(zé)任權(quán)限,需要的朋友可以參考下2016-02-02如何使用jmeter錄制瀏覽器Https請(qǐng)求過(guò)程圖解
這篇文章主要介紹了基于jmeter錄制瀏覽器Https請(qǐng)求過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04SpringBoot + MapStruct 屬性映射工具的使用詳解
MapStruct 是一個(gè)代碼生成器,簡(jiǎn)化了不同的 Java Bean 之間映射的處理,所謂的映射指的就是從一個(gè)實(shí)體變化成一個(gè)實(shí)體。接下來(lái)通過(guò)本文給大家介紹SpringBoot + MapStruct 屬性映射工具的使用,需要的朋友可以參考下2021-09-09Gradle構(gòu)建基本的Web項(xiàng)目結(jié)構(gòu)
這篇文章主要為大家介紹了Gradle創(chuàng)建Web項(xiàng)目基本的框架結(jié)構(gòu)搭建,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03帶你詳細(xì)了解Spring Security的注解方式開(kāi)發(fā)
這篇文章主要介紹了詳解spring security四種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08Java 本地方法Native Method詳細(xì)介紹
這篇文章主要介紹了 Java 本地方法Native Method詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02