跟着刚哥梳理java知识点——多线程(十六)
創建多線程
第一種方式:
① 繼承:繼承Thread。
② 重寫:重寫Thread類的run()方法
③ 創建:創建一個子類的對象
④ 調用:調用線程的start()方法,啟動此線程,調用run()方法
輸出結果:
main:1 main:2 main:3 main:4 Thread-0:1 Thread-0:2 Thread-0:3 Thread-0:4思考:把上面的start修改成run,想想會有什么結果呢?
main:1 main:2 main:3 main:4 main:1 main:2 main:3 main:4走了兩遍的main。因為Start是啟動線程,run只是正常的調用了一下方法,和多線程沒關系。
第二種方法:
① 實現接口:實現Runnable接口的類
② 實現抽象方法:實現接口的run的方法
③ 創建對象:創建一個Runnable接口實現類的對象
④ 放入構造器:將此對象作為形參傳遞給Thread的構造器,創建Thread對象
⑤ 啟動線程:啟動這個線程
?
Thread(類) VS Runnable(接口)
① Runnable避免了java類的單繼承局限性,接口可以多繼承。
② 如果多個線程操作同一份資源更適合使用Runnable的方式
線程Thread的常用方法:
① start():啟動線程并執行相應的run()方法
② run():將子線程要執行的代碼放入run()方法
③ currentThread():靜態的,調取當前的線程
? ? √ getName():獲取此線程的名字
例如:Thread.currentThread().getName()
? ? √ setName():設置線程的名字
④ yield():強制釋放當前cpu執行權,(例如子線程和主線程都循環輸出100次的數字,當主線程%10==0的時候,就調用主線程yield方法Thread.currentThread().yield(),強制主線程釋放CPU執行權)需要說明的是釋放線程的CPU執行權不代表其他線程就一定能搶到CPU的執行權。也可能釋放的線程再次搶到資源。
⑤ join():在A線程中調用B線程join(參與進來的意思)方法,表示當執行到此方法,A線程停止執行,B執行完畢后,A再執行。
⑥ sleep():顯式的讓當前線程睡眠1毫秒
設置線程的優先級:優先級高只能說明搶到的幾率高,不代表一定先完成
① getPriority():獲取線程的優先級
② setPriority():設置線程的優先級
一共是10個等級.默認是等級5,Thread里的屬性就是等級級別
Thread屬性:
√ MAX_PRIORITY:最高的線程優先級
√ MIN_PRIORITY:最低的線程優先級
√ NORM_PRIORITY:默認的線程優先級
線程分為兩類
①守護線程
? ?用來服務用戶的,垃圾回收就是一個典型的守護線程
? ?若JVM都是守護線程,當前JVM將退出
②用戶線程
? ?用戶自己創建的線程
? ?用戶線程-->守護線程:?
? ?通過在start()方法前調用thread.setDaemon(True)就可以
線程的生命周期:枚舉status代表了狀態
1、新建狀態(New):新創建了一個線程對象。
2、就緒狀態(Runnable):線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權。
3、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
? ?(一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
? ?(二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
? ?(三)、其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
線程安全性
原因:
由于一個線程在操作共享數據過程中未執行完的情況下,另外的線程參與進來,
導致共享數據存在安全問題。
共享數據:多個線程共共同操作同一個數據(變量)
解決:
必須讓一個線程操作共享數據完畢之后,其他線程才有機會共享數據的操作
java如何解決的呢?
方式一:同步代碼塊
synchronized(同步監視器){
//需要同步的代碼
}
同步監視器知識點:對于一般的方法,同步監視器可以考慮使用this關鍵字。synchronized(this)
? ? ? ? ? ? ?對于靜態方法而言,同步監視器使用當前類本身充當鎖。synchronized(Singleton.class)
同步塊包住誰呢?誰操作共享數據就包誰
注意:在實現的方式中,可以使用this充當鎖,但是在繼承的方式中,慎用this
單例模式:線程安全
1 class Singleton{ 2 private Singleton(){ 3 4 } 5 private static volatile Singleton instance = null; 6 public static Singleton getInstance(){ 7 if(instance == null){ 8 synchronize(Singleton.calss){ 9 if(instance == null){ 10 instance = new Singleton(); 11 } 12 } 13 } 14 return instance; 15 }方式二:同步方法
同步方法的鎖:this
面試題:
銀行有一個賬號,有兩個儲戶分別向一個賬戶存入3000元,每次存1000,存3次
,每次存完后打印賬戶余額
分析:
共享資源是什么?顯然是一個賬號。
是否需要用多線程?顯然是用的,因為有兩個儲戶
上面代碼需要需要的是:由于是兩個類,一定要保證共享資源類千萬不要被多次實例化
所以一定要讓第一個類實例化完成后當成形參出入到第二個中構造(看紅色標記部分)
線程通信:(三個關鍵字使用的話,都必須在同步代碼塊或同步方法中,三個方法之所以不放在上面的Thread類的方法里,是因為這三個方法是在object里的方法)
① wait():令當前線程掛起并放棄CPU、同步資源。讓別的線程可訪問并修改共享資源,而當前前程排隊等候再次對資源的訪問
② notify():喚醒正在排隊等候同步資源的線程中優先級最高的鎖
③ notifyAll():喚醒所有正在排隊的等待的所有線程結束等待
?
面試題:
1、什么是線程?線程和進程的區別?
進程:是是程序的一次動態執行,它經歷了從代碼加載,執行,到執行完畢的一個完整過程。這個過程也是進程本身從產生、發展,到最終消亡的一個的生命周期。
? ? ? 線程:可以理解為進程的多條執行線索,每條線索又對應著各自獨立的生命周期。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以并發執行。
2、用戶線程和守護線程的區別?
用戶線程和守護線程都是線程,區別是Java虛擬機在所有用戶線程dead后,程序就會結束。而不管是否還有守護線程還在運行,若守護線程還在運行,則會馬上結束。很好理解,守護線程是用來輔助用戶線程的,如公司的保安 和員工,各司其職,當員工都離開后,保安自然下班了。
3、創建線程有哪幾種方式?
這里就不說了,上面已經梳理了。
4、可以直接調用Thread類里的run()方法嗎?
當然可以調用,就是不會啟動線程。
5、你如何理解多線程的優先級的?
在操作系統中,線程可以劃分優先級,優先級較高的線程得到CPU資源較多,也就是CPU優先執行優先級較高的線程對象中的任務(其實并不是這樣)。
在Java中,線程的優先級用setPriority()方法就行,線程的優先級分為1-10這10個等級,如果小于1或大于10,則拋出異常throw new IllegalArgumentException(),默認是5。
線程的優先級仍然無法保障線程的執行次序。只不過,優先級高的線程獲取CPU資源的概率較大,優先級低的并非沒機會執行。
6、線程之間如何同步的?
Java學習筆記---多線程同步的五種方法
7、如何確保線程安全?如何在兩個線程之間共享數據?
JAVA 并發編程-多個線程之間共享數據(六)
8、volatile關鍵字在Java中有什么作用?
java中volatile關鍵字的含義
9、同步方法和同步塊哪個更好?
同步是一個高開銷的操作,因此應該盡量減少同步的內容,故同步塊是更好的選擇,因為它不會鎖住整個對象。同步方法會鎖住整個對象,這通常會導致他們停止執行并需要等待獲得這個對象上的鎖。
10、什么是死鎖?如何避免?
死鎖:如果線程1鎖住了A,然后嘗試對B進行加鎖,同時線程2已經鎖住了B,接著嘗試對A進行加鎖,這時死鎖就發生了。
如何避免:
① 加鎖順序:如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發生
② 加鎖時限:在嘗試獲取鎖的時候加一個超時時間,這也就意味著在嘗試獲取鎖的過程中若超過了這個時限該線程則放棄對該鎖請求
③ 死鎖檢測:當獲取鎖的時候做好記錄,釋放鎖的時候做好記錄,一旦檢測到有死鎖發生的時候,就要對進行回退操作。
11、什么是樂觀鎖和悲觀鎖?
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似于write_condition機制的其實都是提供的樂觀鎖。
? 兩種鎖各有優缺點,不可認為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生沖突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
12、什么是線程池?為什么要使用它?
創建線程要花費昂貴的資源和時間,如果任務來了才創建線程那么響應時間會變長,而且一個進程能創建的線程數有限。為了避免這些問題,在程序啟動的時候就創建若干線程來響應處理,它們被稱為線程池,里面的線程叫工作線程。從JDK1.5開始,Java API提供了Executor框架讓你可以創建不同的線程池。比如單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合很多生存期短的任務的程序的可擴展線程池)。
13、Java Concurrency API中的Lock接口(Lock interface)是什么?對比同步它有什么優勢?
Java中的鎖-Lock接口解析
14、什么是阻塞隊列?如何使用阻塞隊列來實現生產者-消費者模型?
JAVA中的阻塞隊列
15、多線程中棧與堆是公有的還是私有的?
棧私有,堆公有
16、寫一個線程安全的單例模式?雙重鎖是做什么用的?
1 public class Singleton { 2 private static volatile Singleton instance; 3 private Singleton (){ 4 } 5 public static Singleton getInstance(){ //對獲取實例的方法進行同步 6 if (instance == null){ 7 synchronized(Singleton.class){ 8 if (instance == null) 9 instance = new Singleton(); 10 } 11 } 12 return instance; 13 } 14 15 }17、有1個全局變量,int?sum=0,運行2個線程,代碼如下
? ? ?for(int?i=1;i<=50;i++){
sum=sum+1;
?}
? ? ? ?問2個線程都運行完之后,sum的取值范圍。
取值范圍:1-100
18、有三個線程T1,T2,T3,怎么確保它們按順序執行
使用join方法即可。代碼:?有三個線程T1 T2 T3,如何保證他們按順序執行
19、編寫一個程序,開啟3個線程,這3個線程的ID分別為A、B、C,每個線程將自己的ID在屏幕上打印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推。
http://www.cnblogs.com/baizhanshi/p/6428810.html
20、子線程循環?10?次,接著主線程循環?20次,接著又回到子線程循環?10?次,接著再回到主線程又循環 20 次,如此循環50次,試寫出代碼。
1 package com.wang.reflect; 2 //編寫功能類,實現子線程和主線程的功能 3 class Function{ 4 private boolean flag=false; 5 //子線程要實現的功能 6 public synchronized void sub(){ 7 while(flag){ 8 try { 9 this.wait(); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 } 14 15 for(int i=0;i<10;i++){ 16 //for循環內定義子線程的功能,這里簡單的假設為打印一句話,主線程同理 17 System.out.println("sub"+i); 18 } 19 20 flag=true; 21 this.notify(); 22 } 23 //主線程要實現的功能 24 public synchronized void main(){ 25 while(!flag){ 26 try { 27 this.wait(); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 } 32 for(int i=0;i<20;i++){ 33 System.out.println("main"+i); 34 } 35 36 flag=false; 37 this.notify(); 38 } 39 40 } 41 42 public class Demo01 { 43 44 public static void main(String[] args) { 45 final Function f=new Function(); 46 new Thread( 47 new Runnable(){ 48 49 @Override 50 public void run() { 51 for(int i=0;i<50;i++){ 52 f.sub(); 53 } 54 } 55 56 } 57 ).start(); 58 59 for(int i=0;i<50;i++){ 60 f.main(); 61 } 62 } 63 }JDK1.5以后,出現了Lock和condition,Lock類似于synchronized功能,用來進行線程同步,Condition功能類似于Object類中的wait和notify方法,用于線程間的通信.上面的代碼可以用Lock和Condition來改進,如下:
1 package com.wang.reflect; 2 3 import java.util.concurrent.locks.Condition; 4 import java.util.concurrent.locks.Lock; 5 import java.util.concurrent.locks.ReentrantLock; 6 7 //編寫功能類,實現子線程和主線程的功能 8 class Function{ 9 private boolean flag=false; 10 11 Lock lock=new ReentrantLock(); 12 Condition con=lock.newCondition(); 13 //子線程要實現的功能 14 public void sub(){ 15 lock.lock(); 16 try { 17 18 while(flag){ 19 try { 20 con.await(); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 27 for(int i=0;i<10;i++){ 28 //for循環內定義子線程的功能,這里簡單的假設為打印一句話,主線程同理 29 System.out.println("sub"+i); 30 } 31 32 flag=true; 33 con.signal(); 34 } finally{ 35 lock.unlock(); 36 } 37 } 38 //主線程要實現的功能 39 public synchronized void main(){ 40 lock.lock(); 41 try { 42 while (!flag) { 43 try { 44 con.await(); 45 } catch (InterruptedException e) { 46 e.printStackTrace(); 47 } 48 } 49 for (int i = 0; i < 20; i++) { 50 System.out.println("main" + i); 51 } 52 flag = false; 53 con.signal(); 54 } finally{ 55 lock.unlock(); 56 } 57 } 59 } 60 62 public class Demo01 { 66 public static void main(String[] args) { 67 final Function f=new Function(); 68 new Thread( 69 new Runnable(){ 70 71 @Override 72 public void run() { 73 for(int i=0;i<50;i++){ 74 f.sub(); 75 } 76 } 77 78 } 79 ).start(); 80 81 for(int i=0;i<50;i++){ 82 f.main(); 83 } 84 } 85 }?
轉載于:https://www.cnblogs.com/hzg110/p/6707025.html
總結
以上是生活随笔為你收集整理的跟着刚哥梳理java知识点——多线程(十六)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAT甲题题解-1001. A+B Fo
- 下一篇: MapReduce中map并行度优化及源