摘要:最近聽很多面試的小伙伴說,網(wǎng)上往往是一篇一篇的多線程的文章,除了書籍沒有什么學(xué)習(xí)多線程的一系列文章。將此線程標(biāo)記為線程或用戶線程。
最近聽很多面試的小伙伴說,網(wǎng)上往往是一篇一篇的Java多線程的文章,除了書籍沒有什么學(xué)習(xí)多線程的一系列文章。但是僅僅憑借一兩篇文章很難對多線程有系統(tǒng)的學(xué)習(xí),而且面試的時候多線程這方面的知識往往也是考察的重點,所以考慮之下決定寫一系列關(guān)于Java多線程的文章。文章參考了高老師的《Java多線程編程核心技術(shù)》。力爭使用最短的篇幅把Java多線程的知識作以系統(tǒng)的講述。
系列文章傳送門:
Java多線程學(xué)習(xí)(一)Java多線程入門
Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1)
java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1)
Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(2)
Java多線程學(xué)習(xí)(三)volatile關(guān)鍵字
本節(jié)思維導(dǎo)圖:
思維導(dǎo)圖源文件+思維導(dǎo)圖軟件關(guān)注微信公眾號:“Java面試通關(guān)手冊”回復(fù)關(guān)鍵字:“Java多線程”免費領(lǐng)取。
何為線程?
線程與進程相似,但線程是一個比進程更小的執(zhí)行單位。一個進程在其執(zhí)行的過程中可以產(chǎn)生多個線程。與進程不同的是同類的多個線程共享同一塊內(nèi)存空間和一組系統(tǒng)資源,所以系統(tǒng)在產(chǎn)生一個線程,或是在各個線程之間作切換工作時,負擔(dān)要比進程小得多,也正因為如此,線程也被稱為輕量級進程。
何為進程?
進程是程序的一次執(zhí)行過程,是系統(tǒng)運行程序的基本單位,因此進程是動態(tài)的。系統(tǒng)運行一個程序即是一個進程從創(chuàng)建,運行到消亡的過程。簡單來說,一個進程就是一個執(zhí)行中的程序,它在計算機中一個指令接著一個指令地執(zhí)行著,同時,每個進程還占有某些系統(tǒng)資源如CPU時間,內(nèi)存空間,文件,文件,輸入輸出設(shè)備的使用權(quán)等等。換句話說,當(dāng)程序在執(zhí)行時,將會被操作系統(tǒng)載入內(nèi)存中。
線程和進程有何不同?
線程是進程劃分成的更小的運行單位。線程和進程最大的不同在于基本上各進程是獨立的,而各線程則不一定,因為同一進程中的線程極有可能會相互影響。從另一角度來說,進程屬于操作系統(tǒng)的范疇,主要是同一段時間內(nèi),可以同時執(zhí)行一個以上的程序,而線程則是在同一程序內(nèi)幾乎同時執(zhí)行一個以上的程序段。
1.2 多線程何為多線程?
多線程就是幾乎同時執(zhí)行多個線程(一個處理器在某一個時間點上永遠都只能是一個線程!即使這個處理器是多核的,除非有多個處理器才能實現(xiàn)多個線程同時運行。)。幾乎同時是因為實際上多線程程序中的多個線程實際上是一個線程執(zhí)行一會然后其他的線程再執(zhí)行,并不是很多書籍所謂的同時執(zhí)行。
為什么多線程是必要的?
使用線程可以把占據(jù)長時間的程序中的任務(wù)放到后臺去處理
用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發(fā)某些事件的處理,可以彈出一個進度條來顯示處理的進度
程序的運行速度可能加快
二 使用多線程 2.1繼承Thread類MyThread.java
public class MyThread extends Thread { @Override public void run() { super.run(); System.out.println("MyThread"); } }
Run.java
public class Run { public static void main(String[] args) { MyThread mythread = new MyThread(); mythread.start(); System.out.println("運行結(jié)束"); } }
運行結(jié)果:
從上面的運行結(jié)果可以看出:線程是一個子任務(wù),CPU以不確定的方式,或者說是以隨機的時間來調(diào)用線程中的run方法。
推薦實現(xiàn)Runnable接口方式開發(fā)多線程,因為Java單繼承但是可以實現(xiàn)多個接口。
MyRunnable.java
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("MyRunnable"); } }
Run.java
public class Run { public static void main(String[] args) { Runnable runnable=new MyRunnable(); Thread thread=new Thread(runnable); thread.start(); System.out.println("運行結(jié)束!"); } }
運行結(jié)果:
定義線程類中的實例變量針對其他線程可以有共享和不共享之分
3.1 不共享數(shù)據(jù)的情況MyThread.java
public class MyThread extends Thread { private int count = 5; public MyThread(String name) { super(); this.setName(name); } @Override public void run() { super.run(); while (count > 0) { count--; System.out.println("由 " + MyThread.currentThread().getName() + " 計算,count=" + count); } } }
Run.java
public class Run { public static void main(String[] args) { MyThread a = new MyThread("A"); MyThread b = new MyThread("B"); MyThread c = new MyThread("C"); a.start(); b.start(); c.start(); } }
運行結(jié)果:
可以看出每個線程都有一個屬于自己的實例變量count,它們之間互不影響。我們再來看看另一種情況。
MyThread.java
public class MyThread extends Thread { private int count = 5; @Override public void run() { super.run(); count--; System.out.println("由 " + MyThread.currentThread().getName() + " 計算,count=" + count); } }
Run.java
public class Run { public static void main(String[] args) { MyThread mythread=new MyThread(); //下列線程都是通過mythread對象創(chuàng)建的 Thread a=new Thread(mythread,"A"); Thread b=new Thread(mythread,"B"); Thread c=new Thread(mythread,"C"); Thread d=new Thread(mythread,"D"); Thread e=new Thread(mythread,"E"); a.start(); b.start(); c.start(); d.start(); e.start(); } }
運行結(jié)果:
可以看出這里已經(jīng)出現(xiàn)了錯誤,我們想要的是依次遞減的結(jié)果。為什么呢??
因為在大多數(shù)jvm中,count--的操作分為如下下三步:
取得原有count值
計算i -1
對i進行賦值
所以多個線程同時訪問時出現(xiàn)問題就是難以避免的了。
那么有沒有什么解決辦法呢?
答案是:當(dāng)然有,而且很簡單。
在run方法前加上synchronized關(guān)鍵字即可得到正確答案。
加上關(guān)鍵字后的運行結(jié)果:
返回對當(dāng)前正在執(zhí)行的線程對象的引用。
4.2 getId()返回此線程的標(biāo)識符
4.3 getName()返回此線程的名稱
4.4 getPriority()返回此線程的優(yōu)先級
4.5 isAlive()測試這個線程是否還處于活動狀態(tài)。
什么是活動狀態(tài)呢?
活動狀態(tài)就是線程已經(jīng)啟動且尚未終止。線程處于正在運行或準(zhǔn)備運行的狀態(tài)。
4.6 sleep(long millis)使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)“休眠”(暫時停止執(zhí)行),具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準(zhǔn)確性。
4.7 interrupt()中斷這個線程。
4.8 interrupted() 和isInterrupted()interrupted():測試當(dāng)前線程是否已經(jīng)是中斷狀態(tài),執(zhí)行后具有將狀態(tài)標(biāo)志清除為false的功能
isInterrupted(): 測試線程Thread對相關(guān)是否已經(jīng)是中斷狀態(tài),但部清楚狀態(tài)標(biāo)志
4.9 setName(String name)將此線程的名稱更改為等于參數(shù) name 。
4.10 isDaemon()測試這個線程是否是守護線程。
4.11 setDaemon(boolean on)將此線程標(biāo)記為 daemon線程或用戶線程。
4.12 join()在很多情況下,主線程生成并起動了子線程,如果子線程里要進行大量的耗時的運算,主線程往往將于子線程之前結(jié)束,但是如果主線程處理完其他的事務(wù)后,需要用到子線程的處理結(jié)果,也就是主線程需要等待子線程執(zhí)行完成之后再結(jié)束,這個時候就要用到j(luò)oin()方法了。
join()的作用是:“等待該線程終止”,這里需要理解的就是該線程是指的主線程等待子線程的終止。也就是在子線程調(diào)用了join()方法后面的代碼,只有等到子線程結(jié)束了才能執(zhí)行
4.13 yield()yield()方法的作用是放棄當(dāng)前的CPU資源,將它讓給其他的任務(wù)去占用CPU時間。注意:放棄的時間不確定,可能一會就會重新獲得CPU時間片。
4.14 setPriority(int newPriority)更改此線程的優(yōu)先級
五 如何停止一個線程呢?stop(),suspend(),resume()(僅用于與suspend()一起使用)這些方法已被棄用,所以我這里不予講解。
我們上面提到了interrupt()方法,先來試一下interrupt()方法能不能停止線程
MyThread.java
public class MyThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 5000000; i++) { System.out.println("i=" + (i + 1)); } } }
Run.java
public class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } } }
運行上訴代碼你會發(fā)現(xiàn),線程并不會終止。
針對上面代碼的一個改進:
interrupted()方法判斷線程是否停止,如果是停止?fàn)顟B(tài)則break
MyThread.java
public class MyThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 500000; i++) { if (this.interrupted()) { System.out.println("已經(jīng)是停止?fàn)顟B(tài)了!我要退出了!"); break; } System.out.println("i=" + (i + 1)); } System.out.println("看到這句話說明線程并未終止------"); } }
Run.java
public class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } }
運行結(jié)果:
for循環(huán)雖然停止執(zhí)行了,但是for循環(huán)下面的語句還是會執(zhí)行,說明線程并未被停止。
MyThread.java
public class MyThread extends Thread { @Override public void run() { while (true) { if (this.isInterrupted()) { System.out.println("??停止了!"); return; } System.out.println("timer=" + System.currentTimeMillis()); } } }
Run.java
public class Run { public static void main(String[] args) throws InterruptedException { MyThread t=new MyThread(); t.start(); Thread.sleep(2000); t.interrupt(); } }
運行結(jié)果:
當(dāng)然還有其他停止線程的方法,后面再做介紹。
每個線程都具有各自的優(yōu)先級,線程的優(yōu)先級可以在程序中表明該線程的重要性,如果有很多線程處于就緒狀態(tài),系統(tǒng)會根據(jù)優(yōu)先級來決定首先使哪個線程進入運行狀態(tài)。但這個并不意味著低
優(yōu)先級的線程得不到運行,而只是它運行的幾率比較小,如垃圾回收機制線程的優(yōu)先級就比較低。所以很多垃圾得不到及時的回收處理。
線程優(yōu)先級具有繼承特性比如A線程啟動B線程,則B線程的優(yōu)先級和A是一樣的。
線程優(yōu)先級具有隨機性也就是說線程優(yōu)先級高的不一定每一次都先執(zhí)行完。
Thread類中包含的成員變量代表了線程的某些優(yōu)先級。如Thread.MIN_PRIORITY(常數(shù)1),Thread.NORM_PRIORITY(常數(shù)5),
Thread.MAX_PRIORITY(常數(shù)10)。其中每個線程的優(yōu)先級都在Thread.MIN_PRIORITY(常數(shù)1) 到Thread.MAX_PRIORITY(常數(shù)10) 之間,在默認情況下優(yōu)先級都是Thread.NORM_PRIORITY(常數(shù)5)。
學(xué)過操作系統(tǒng)這門課程的話,我們可以發(fā)現(xiàn)多線程優(yōu)先級或多或少借鑒了操作系統(tǒng)對進程的管理。
線程優(yōu)先級具有繼承特性測試代碼:
MyThread1.java:
public class MyThread1 extends Thread { @Override public void run() { System.out.println("MyThread1 run priority=" + this.getPriority()); MyThread2 thread2 = new MyThread2(); thread2.start(); } }
MyThread2.java:
public class MyThread2 extends Thread { @Override public void run() { System.out.println("MyThread2 run priority=" + this.getPriority()); } }
Run.java:
public class Run { public static void main(String[] args) { System.out.println("main thread begin priority=" + Thread.currentThread().getPriority()); Thread.currentThread().setPriority(6); System.out.println("main thread end priority=" + Thread.currentThread().getPriority()); MyThread1 thread1 = new MyThread1(); thread1.start(); } }
運行結(jié)果:
用戶線程:運行在前臺,執(zhí)行具體的任務(wù),如程序的主線程、連接網(wǎng)絡(luò)的子線程等都是用戶線程
守護線程:運行在后臺,為其他前臺線程服務(wù).也可以說守護線程是JVM中非守護線程的 “傭人”。
特點:一旦所有用戶線程都結(jié)束運行,守護線程會隨JVM一起結(jié)束工作
應(yīng)用:數(shù)據(jù)庫連接池中的檢測線程,JVM虛擬機啟動后的檢測線程
最常見的守護線程:垃圾回收線程
7.2 如何設(shè)置守護線程?可以通過調(diào)用Thead類的setDaemon(true)方法設(shè)置當(dāng)前的線程為守護線程
注意事項:
1. setDaemon(true)必須在start()方法前執(zhí)行,否則會拋出IllegalThreadStateException異常 2. 在守護線程中產(chǎn)生的新線程也是守護線程 3. 不是所有的任務(wù)都可以分配給守護線程來執(zhí)行,比如讀寫操作或者計算邏輯
MyThread.java:
public class MyThread extends Thread { private int i = 0; @Override public void run() { try { while (true) { i++; System.out.println("i=" + (i)); Thread.sleep(100); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Run.java:
public class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.setDaemon(true); thread.start(); Thread.sleep(5000); System.out.println("我離開thread對象也不再打印了,也就是停止了!"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
運行結(jié)果:
如果你覺得博主的文章不錯,歡迎轉(zhuǎn)發(fā)點贊。你能從中學(xué)到知識就是我最大的幸運。
歡迎關(guān)注我的微信公眾號:“Java面試通關(guān)手冊”(分享各種Java學(xué)習(xí)資源,面試題,以及企業(yè)級Java實戰(zhàn)項目回復(fù)關(guān)鍵字免費領(lǐng)取):
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68952.html
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進步歡迎點贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:學(xué)習(xí)完多線程之后可以通過下面這些問題檢測自己是否掌握,下面這些問題的答案以及常見多線程知識點的總結(jié)在這里。可選數(shù)據(jù)結(jié)構(gòu)與算法如果你想進入大廠的話,我推薦你在學(xué)習(xí)完基礎(chǔ)或者多線程之后,就開始每天抽出一點時間來學(xué)習(xí)算法和數(shù)據(jù)結(jié)構(gòu)。 我自己總結(jié)的Java學(xué)習(xí)的系統(tǒng)知識點以及面試問題,已經(jīng)開源,目前已經(jīng) 35k+ Star。會一直完善下去,歡迎建議和指導(dǎo),同時也歡迎Star: https://...
摘要:我的學(xué)習(xí)筆記匯總標(biāo)簽筆記分為兩大部分和筆記內(nèi)容主要是對一些基礎(chǔ)特性和編程細節(jié)進行總結(jié)整理,適合了解基礎(chǔ)語法,想進一步深入學(xué)習(xí)的人如果覺得不錯,請給,這也是對我的鼓勵,有什么意見歡迎留言反饋目錄基礎(chǔ)鞏固筆記反射基礎(chǔ)鞏固筆記泛型基礎(chǔ)鞏 我的java&javaweb學(xué)習(xí)筆記(匯總) 標(biāo)簽: java [TOC] 筆記分為兩大部分:javase和javaweb javase javawe...
閱讀 3753·2021-09-09 09:33
閱讀 3035·2019-08-30 15:56
閱讀 3032·2019-08-30 15:56
閱讀 3320·2019-08-30 15:55
閱讀 509·2019-08-30 15:53
閱讀 2191·2019-08-30 15:52
閱讀 679·2019-08-28 18:16
閱讀 2418·2019-08-26 13:51