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