Java 多線程實(shí)例詳解(二)
本文承接上一篇文章《Java多線程實(shí)例詳解(一)》。
四.Java多線程的阻塞狀態(tài)與線程控制
上文已經(jīng)提到Java阻塞的幾種具體類型。下面分別看下引起Java線程阻塞的主要方法。
1.join()
join —— 讓一個線程等待另一個線程完成才繼續(xù)執(zhí)行。如A線程線程執(zhí)行體中調(diào)用B線程的join()方法,則A線程被阻塞,知道B線程執(zhí)行完為止,A才能得以繼續(xù)執(zhí)行。
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { thread.start(); try { thread.join(); // main線程需要等待thread線程執(zhí)行完后才能繼續(xù)執(zhí)行 } catch (InterruptedException e) { e.printStackTrace(); } } } } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } }
2.sleep()
sleep —— 讓當(dāng)前的正在執(zhí)行的線程暫停指定的時間,并進(jìn)入阻塞狀態(tài)。在其睡眠的時間段內(nèi),該線程由于不是處于就緒狀態(tài),因此不會得到執(zhí)行的機(jī)會。即使此時系統(tǒng)中沒有任何其他可執(zhí)行的線程,出于sleep()中的線程也不會執(zhí)行。因此sleep()方法常用來暫停線程執(zhí)行。
前面有講到,當(dāng)調(diào)用了新建的線程的start()方法后,線程進(jìn)入到就緒狀態(tài),可能會在接下來的某個時間獲取CPU時間片得以執(zhí)行,如果希望這個新線程必然性的立即執(zhí)行,直接調(diào)用原來線程的sleep(1)即可。
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { thread.start(); try { Thread.sleep(1); // 使得thread必然能夠馬上得以執(zhí)行 } catch (InterruptedException e) { e.printStackTrace(); } } } } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } }
注:睡一個毫秒級夠了,因?yàn)镃PU不會空閑,會切換到新建的線程。
3.后臺線程(Daemon Thread)
概念/目的:后臺線程主要是為其他線程(相對可以稱之為前臺線程)提供服務(wù),或“守護(hù)線程”。如JVM中的垃圾回收線程。
生命周期:后臺線程的生命周期與前臺線程生命周期有一定關(guān)聯(lián)。主要體現(xiàn)在:當(dāng)所有的前臺線程都進(jìn)入死亡狀態(tài)時,后臺線程會自動死亡(其實(shí)這個也很好理解,因?yàn)楹笈_線程存在的目的在于為前臺線程服務(wù)的,既然所有的前臺線程都死亡了,那它自己還留著有什么用...偉大啊 ! !)。
設(shè)置后臺線程:調(diào)用Thread對象的setDaemon(true)方法可以將指定的線程設(shè)置為后臺線程。
public class ThreadTest { public static void main(String[] args) { Thread myThread = new MyThread(); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread.setDaemon(true); myThread.start(); } } } } class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("i = " + i); try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
判斷線程是否是后臺線程:調(diào)用thread對象的isDeamon()方法。
注:main線程默認(rèn)是前臺線程,前臺線程創(chuàng)建中創(chuàng)建的子線程默認(rèn)是前臺線程,后臺線程中創(chuàng)建的線程默認(rèn)是后臺線程。調(diào)用setDeamon(true)方法將前臺線程設(shè)置為后臺線程時,需要在start()方法調(diào)用之前。前天線程都死亡后,JVM通知后臺線程死亡,但從接收指令到作出響應(yīng),需要一定的時間。
4.改變線程的優(yōu)先級/setPriority():
每個線程在執(zhí)行時都具有一定的優(yōu)先級,優(yōu)先級高的線程具有較多的執(zhí)行機(jī)會。每個線程默認(rèn)的優(yōu)先級都與創(chuàng)建它的線程的優(yōu)先級相同。main線程默認(rèn)具有普通優(yōu)先級。
設(shè)置線程優(yōu)先級:setPriority(int priorityLevel)。參數(shù)priorityLevel范圍在1-10之間,常用的有如下三個靜態(tài)常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
獲取線程優(yōu)先級:getPriority()。
注:具有較高線程優(yōu)先級的線程對象僅表示此線程具有較多的執(zhí)行機(jī)會,而非優(yōu)先執(zhí)行。
public class ThreadTest { public static void main(String[] args) { Thread myThread = new MyThread(); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread.setPriority(Thread.MAX_PRIORITY); myThread.start(); } } } } class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("i = " + i); } } }
5.線程讓步:yield()
上一篇博文中已經(jīng)講到了yield()的基本作用,同時,yield()方法還與線程優(yōu)先級有關(guān),當(dāng)某個線程調(diào)用yiled()方法從運(yùn)行狀態(tài)轉(zhuǎn)換到就緒狀態(tài)后,CPU從就緒狀態(tài)線程隊(duì)列中只會選擇與該線程優(yōu)先級相同或優(yōu)先級更高的線程去執(zhí)行。
public class ThreadTest { public static void main(String[] args) { Thread myThread1 = new MyThread1(); Thread myThread2 = new MyThread2(); myThread1.setPriority(Thread.MAX_PRIORITY); myThread2.setPriority(Thread.MIN_PRIORITY); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread1.start(); myThread2.start(); Thread.yield(); } } } } class MyThread1 extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("myThread 1 -- i = " + i); } } } class MyThread2 extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("myThread 2 -- i = " + i); } } }
系列文章:
java 多線程實(shí)例講解 (一)
Java 多線程實(shí)例詳解(二)
Java 多線程實(shí)例詳解(三)
相關(guān)文章
解決idea spring boot 修改html等不重啟即時生效的問題
這篇文章主要介紹了解決idea spring boot 修改html等不重啟即時生效的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Springboot MongoDB實(shí)現(xiàn)自增序列的項(xiàng)目實(shí)踐
在某些特定的業(yè)務(wù)場景下,會需要使用自增的序列來維護(hù)數(shù)據(jù),本文主要介紹了Springboot MongoDB實(shí)現(xiàn)自增序列的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07springboot中rabbitmq實(shí)現(xiàn)消息可靠性機(jī)制詳解
這篇文章主要介紹了springboot中rabbitmq實(shí)現(xiàn)消息可靠性機(jī)制詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09Mybatis中l(wèi)ike搭配concat的寫法詳解
這篇文章主要介紹了Mybatis中l(wèi)ike搭配concat的寫法詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01windows下java環(huán)境變量的設(shè)置方法
在“系統(tǒng)變量”中,設(shè)置3項(xiàng)屬性,JAVA_HOME,PATH,CLASSPATH(大小寫無所謂),若已存在則點(diǎn)擊“編輯”,不存在則點(diǎn)擊“新建”2013-09-09Spring中的監(jiān)聽器SpringApplicationRunListener詳解
這篇文章主要介紹了Spring中的監(jiān)聽器SpringApplicationRunListener詳解,命名我們就可以知道它是一個監(jiān)聽者,分析springboot啟動流程我們會發(fā)現(xiàn),它其實(shí)是用來在整個啟動流程中接收不同執(zhí)行點(diǎn)事件通知的監(jiān)聽者,需要的朋友可以參考下2023-11-11