摘要:現(xiàn)在已經(jīng)沒有強(qiáng)制線程終止的方法了由于方法可以讓一個線程終止掉另一個線程被終止的線程會立即釋放鎖,這可能會讓對象處于不一致的狀態(tài)。
前言
昨天已經(jīng)寫了:
多線程三分鐘就可以入個門了!
如果沒看的同學(xué)建議先去閱讀一遍哦~
在寫文章之前通讀了一遍《Java 核心技術(shù) 卷一》的并發(fā)章節(jié)和《Java并發(fā)編程實(shí)戰(zhàn)》前面的部分,回顧了一下以前寫過的筆記。從今天開始進(jìn)入多線程的知識點(diǎn)咯~
我其實(shí)也是相當(dāng)于從零開始學(xué)多線程的,如果文章有錯的地方還請大家多多包含,不吝在評論區(qū)下指正呢~~
一、Thread線程類API聲明本文使用的是JDK1.8
實(shí)現(xiàn)多線程從本質(zhì)上都是由Thread類來進(jìn)行操作的~我們來看看Thread類一些重要的知識點(diǎn)。Thread這個類很大,不可能整個把它看下來,只能看一些常見的、重要的方法。
頂部注釋的我們已經(jīng)解析過了,如果不知道的同學(xué)可前往:多線程三分鐘就可以入個門了!
1.1設(shè)置線程名我們在使用多線程的時候,想要查看線程名是很簡單的,調(diào)用Thread.currentThread().getName()即可。
如果沒有做什么的設(shè)置,我們會發(fā)現(xiàn)線程的名字是這樣子的:主線程叫做main,其他線程是Thread-x
下面我就帶著大家來看看它是怎么命名的:
nextThreadNum()的方法實(shí)現(xiàn)是這樣的:
基于這么一個變量-->線程初始化的數(shù)量
點(diǎn)進(jìn)去看到init方法就可以確定了:
看到這里,如果我們想要為線程起個名字,那也是很簡單的。Thread給我們提供了構(gòu)造方法!
下面我們來測試一下:
實(shí)現(xiàn)了Runnable的方式來實(shí)現(xiàn)多線程:
public class MyThread implements Runnable { @Override public void run() { // 打印出當(dāng)前線程的名字 System.out.println(Thread.currentThread().getName()); } }
測試:
public class MyThreadDemo { public static void main(String[] args) { MyThread myThread = new MyThread(); //帶參構(gòu)造方法給線程起名字 Thread thread1 = new Thread(myThread, "關(guān)注公眾號Java3y"); Thread thread2 = new Thread(myThread, "qq群:742919422"); thread1.start(); thread2.start(); // 打印當(dāng)前線程的名字 System.out.println(Thread.currentThread().getName()); } }
結(jié)果:
當(dāng)然了,我們還可以通過setName(String name)的方法來改掉線程的名字的。我們來看看方法實(shí)現(xiàn);
檢查是否有權(quán)限修改:
至于threadStatus這個狀態(tài)屬性,貌似沒發(fā)現(xiàn)他會在哪里修改:
1.2守護(hù)線程守護(hù)線程是為其他線程服務(wù)的
垃圾回收線程就是守護(hù)線程~
守護(hù)線程有一個特點(diǎn):
當(dāng)別的用戶線程執(zhí)行完了,虛擬機(jī)就會退出,守護(hù)線程也就會被停止掉了。
也就是說:守護(hù)線程作為一個服務(wù)線程,沒有服務(wù)對象就沒有必要繼續(xù)運(yùn)行了
使用線程的時候要注意的地方
在線程啟動前設(shè)置為守護(hù)線程,方法是setDaemon(boolean on)
使用守護(hù)線程不要訪問共享資源(數(shù)據(jù)庫、文件等),因?yàn)樗赡軙谌魏螘r候就掛掉了。
守護(hù)線程中產(chǎn)生的新線程也是守護(hù)線程
測試一波:
public class MyThreadDemo { public static void main(String[] args) { MyThread myThread = new MyThread(); //帶參構(gòu)造方法給線程起名字 Thread thread1 = new Thread(myThread, "關(guān)注公眾號Java3y"); Thread thread2 = new Thread(myThread, "qq群:742919422"); // 設(shè)置為守護(hù)線程 thread2.setDaemon(true); thread1.start(); thread2.start(); System.out.println(Thread.currentThread().getName()); } }
上面的代碼運(yùn)行多次可以出現(xiàn)(電腦性能足夠好的同學(xué)可能測試不出來):線程1和主線程執(zhí)行完了,我們的守護(hù)線程就不執(zhí)行了~
原理:這也就為什么我們要在啟動之前設(shè)置守護(hù)線程了。
1.3優(yōu)先級線程線程優(yōu)先級高僅僅表示線程獲取的CPU時間片的幾率高,但這不是一個確定的因素!
線程的優(yōu)先級是高度依賴于操作系統(tǒng)的,Windows和Linux就有所區(qū)別(Linux下優(yōu)先級可能就被忽略了)~
可以看到的是,Java提供的優(yōu)先級默認(rèn)是5,最低是1,最高是10:
實(shí)現(xiàn):
setPriority0是一個本地(navite)的方法:
private native void setPriority0(int newPriority);1.4線程生命周期
在上一篇介紹的時候其實(shí)也提過了線程的線程有3個基本狀態(tài):執(zhí)行、就緒、阻塞
在Java中我們就有了這個圖,Thread上很多的方法都是用來切換線程的狀態(tài)的,這一部分是重點(diǎn)!
其實(shí)上面這個圖是不夠完整的,省略掉了一些東西。后面在講解的線程狀態(tài)的時候我會重新畫一個~
下面就來講解與線程生命周期相關(guān)的方法~
1.4.1sleep方法調(diào)用sleep方法會進(jìn)入計時等待狀態(tài),等時間到了,進(jìn)入的是就緒狀態(tài)而并非是運(yùn)行狀態(tài)!
于是乎,我們的圖就可以補(bǔ)充成這樣:
1.4.2yield方法調(diào)用yield方法會先讓別的線程執(zhí)行,但是不確保真正讓出
意思是:我有空,可以的話,讓你們先執(zhí)行
于是乎,我們的圖就可以補(bǔ)充成這樣:
1.4.3join方法調(diào)用join方法,會等待該線程執(zhí)行完畢后才執(zhí)行別的線程~
我們進(jìn)去看看具體的實(shí)現(xiàn):
wait方法是在Object上定義的,它是native本地方法,所以就看不了了:
wait方法實(shí)際上它也是計時等待(如果帶時間參數(shù))的一種!,于是我們可以補(bǔ)充我們的圖:
1.4.3interrupt方法線程中斷在之前的版本有stop方法,但是被設(shè)置過時了?,F(xiàn)在已經(jīng)沒有強(qiáng)制線程終止的方法了!
由于stop方法可以讓一個線程A終止掉另一個線程B
被終止的線程B會立即釋放鎖,這可能會讓對象處于不一致的狀態(tài)。
線程A也不知道線程B什么時候能夠被終止掉,萬一線程B還處理運(yùn)行計算階段,線程A調(diào)用stop方法將線程B終止,那就很無辜了~
總而言之,Stop方法太暴力了,不安全,所以被設(shè)置過時了。
我們一般使用的是interrupt來請求終止線程~
要注意的是:interrupt不會真正停止一個線程,它僅僅是給這個線程發(fā)了一個信號告訴它,它應(yīng)該要結(jié)束了(明白這一點(diǎn)非常重要!)
也就是說:Java設(shè)計者實(shí)際上是想線程自己來終止,通過上面的信號,就可以判斷處理什么業(yè)務(wù)了。
具體到底中斷還是繼續(xù)運(yùn)行,應(yīng)該由被通知的線程自己處理
Thread t1 = new Thread( new Runnable(){ public void run(){ // 若未發(fā)生中斷,就正常執(zhí)行任務(wù) while(!Thread.currentThread.isInterrupted()){ // 正常任務(wù)代碼…… } // 中斷的處理代碼…… doSomething(); } } ).start();
再次說明:調(diào)用interrupt()并不是要真正終止掉當(dāng)前線程,僅僅是設(shè)置了一個中斷標(biāo)志。這個中斷標(biāo)志可以給我們用來判斷什么時候該干什么活!什么時候中斷由我們自己來決定,這樣就可以安全地終止線程了!
我們來看看源碼是怎么講的吧:
再來看看剛才說拋出的異常是什么東東吧:
所以說:interrupt方法壓根是不會對線程的狀態(tài)造成影響的,它僅僅設(shè)置一個標(biāo)志位罷了
interrupt線程中斷還有另外兩個方法(檢查該線程是否被中斷):
靜態(tài)方法interrupted()-->會清除中斷標(biāo)志位
實(shí)例方法isInterrupted()-->不會清除中斷標(biāo)志位
上面還提到了,如果阻塞線程調(diào)用了interrupt()方法,那么會拋出異常,設(shè)置標(biāo)志位為false,同時該線程會退出阻塞的。我們來測試一波:
public class Main { /** * @param args */ public static void main(String[] args) { Main main = new Main(); // 創(chuàng)建線程并啟動 Thread t = new Thread(main.runnable); System.out.println("This is main "); t.start(); try { // 在 main線程睡個3秒鐘 Thread.sleep(3000); } catch (InterruptedException e) { System.out.println("In main"); e.printStackTrace(); } // 設(shè)置中斷 t.interrupt(); } Runnable runnable = () -> { int i = 0; try { while (i < 1000) { // 睡個半秒鐘我們再執(zhí)行 Thread.sleep(500); System.out.println(i++); } } catch (InterruptedException e) { // 判斷該阻塞線程是否還在 System.out.println(Thread.currentThread().isAlive()); // 判斷該線程的中斷標(biāo)志位狀態(tài) System.out.println(Thread.currentThread().isInterrupted()); System.out.println("In Runnable"); e.printStackTrace(); } }; }
結(jié)果:
接下來我們分析它的執(zhí)行流程是怎么樣的:
2018年4月18日20:32:15(哇,這個方法真的消耗了我非常長的時間).....感謝@開始de痕跡的指教~
該參考資料:
https://www.cnblogs.com/w-wfy/p/6414801.html
https://www.cnblogs.com/carmanloneliness/p/3516405.html
https://www.zhihu.com/question/41048032/answer/89478427
https://www.zhihu.com/question/41048032/answer/89431513
二、總結(jié)可以發(fā)現(xiàn)我們的圖是還沒有補(bǔ)全的~后續(xù)的文章講到同步的時候會繼續(xù)使用上面的圖的。在Thread中重要的還是那幾個可以切換線程狀態(tài)的方法,還有理解中斷的真正含義。
使用線程會導(dǎo)致我們數(shù)據(jù)不安全,甚至程序無法運(yùn)行的情況的,這些問題都會再后面講解到的~
之前在學(xué)習(xí)操作系統(tǒng)的時候根據(jù)《計算機(jī)操作系統(tǒng)-湯小丹》這本書也做了一點(diǎn)點(diǎn)筆記,都是比較淺顯的知識點(diǎn)?;蛟S對大家有幫助
操作系統(tǒng)第一篇【引論】
操作系統(tǒng)第二篇【進(jìn)程管理】
操作系統(tǒng)第三篇【線程】
操作系統(tǒng)第四篇【處理機(jī)調(diào)度】
操作系統(tǒng)第五篇【死鎖】
操作系統(tǒng)第六篇【存儲器管理】
操作系統(tǒng)第七篇【設(shè)備管理】
參考資料:
《Java核心技術(shù)卷一》
《Java并發(fā)編程實(shí)戰(zhàn)》
《計算機(jī)操作系統(tǒng)-湯小丹》
如果文章有錯的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號:Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導(dǎo)航:
https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69124.html
Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley是Google在2013年推出來的HTTP庫,旨在幫助開發(fā)者更快更簡便的實(shí)現(xiàn)網(wǎng)絡(luò)請求。說說為什么要分析Volley的源碼吧,因?yàn)閂olley中線程的轉(zhuǎn)換時通過 Thread 和 Ha...
摘要:系統(tǒng)級線程核心級線程由操作系統(tǒng)內(nèi)核進(jìn)行管理。值得注意的是多線程的存在,不是提高程序的執(zhí)行速度。實(shí)現(xiàn)多線程上面說了一大堆基礎(chǔ),理解完的話。虛擬機(jī)的啟動是單線程的還是多線程的是多線程的。 前言 之前花了一個星期回顧了Java集合: Collection總覽 List集合就這么簡單【源碼剖析】 Map集合、散列表、紅黑樹介紹 HashMap就是這么簡單【源碼剖析】 LinkedHashMa...
摘要:等到所有子線程都執(zhí)行完后即,會主調(diào)用線程,然后主調(diào)用線程就會從函數(shù)返回,繼續(xù)后余動作。 原理剖析(第 005 篇)AQS工作原理分析 - 一、大致介紹 1、前面章節(jié)講解了一下CAS,簡單講就是cmpxchg+lock的原子操作; 2、而在談到并發(fā)操作里面,我們不得不談到AQS,JDK的源碼里面好多并發(fā)的類都是通過Sync的內(nèi)部類繼承AQS而實(shí)現(xiàn)出五花八門的功能; 3、本章節(jié)就和大家分享...
閱讀 3174·2021-11-19 09:40
閱讀 3664·2021-11-16 11:52
閱讀 2992·2021-11-11 16:55
閱讀 3191·2019-08-30 15:55
閱讀 1195·2019-08-30 13:08
閱讀 1667·2019-08-29 17:03
閱讀 3026·2019-08-29 16:19
閱讀 2589·2019-08-29 13:43