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