標籤:str 線程 min 守護 介面實現 override extends private for
建立多線程
第一種方式:
① 繼承:繼承Thread。
② 重寫:重寫Thread類的run()方法
③ 建立:建立一個子類的對象
④ 調用:調用線程的start()方法,啟動此線程,調用run()方法
1 class Work extends Thread{ //① 繼承 2 @Override 3 //② 重寫 4 public void run() { 5 for (int i = 1 ;i < 5; i++) { 6 System.out.println(Thread.currentThread().getName()+":"+i); 7 } 8 } 9 }10 public static void main(String[] args) {11 //③ 建立12 Work work = new Work();13 //④ 調用14 work.start();15 for (int i = 1 ;i < 5; i++) {16 System.out.println(Thread.currentThread().getName()+":"+i);17 }18 }
輸出結果:
main:1main:2main:3main:4Thread-0:1Thread-0:2Thread-0:3Thread-0:4
思考:把上面的start修改成run,想想會有什麼結果呢?
main:1main:2main:3main:4main:1main:2main:3main:4
走了兩遍的main。因為Start是啟動線程,run只是正常的調用了一下方法,和多線程沒關係。
第二種方法:
① 實現介面:實現Runnable介面的類
② 實現抽象方法:實現介面的run的方法
③ 建立對象:建立一個Runnable介面實作類別的對象
④ 放入構造器:將此對象作為形參傳遞給Thread的構造器,建立Thread對象
⑤ 啟動線程:啟動這個線程
1 class Work implements Runnable{ //① 實現介面 2 //② 實現抽象方法 3 public void run() { 4 for (int i = 11 ;i < 100; i++) { 5 System.out.println(Thread.currentThread().getName()+":"+i); 6 } 7 } 8 } 9 10 public static void main(String[] args) {11 //③ 建立對象12 Work work = new Work();13 //④ 放入構造器14 Thread t1 = new Thread(work);15 //⑤ 啟動線程16 t1.start();17 for (int i = 11 ;i < 100; i++) {18 System.out.println(Thread.currentThread().getName()+":"+i);19 }20 }
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充當鎖,但是在繼承的方式中,慎用this
方式二:同步方法
同步方法的鎖:this
public synchronize void 方法(){}
單例模式:安全執行緒
1 class Singleton{ 2 private Singleton(){ 3 4 } 5 private static 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 }
面試題:
銀行有一個帳號,有兩個儲戶分別向一個賬戶存入3000元,每次存1000,存3次
,每次存完後列印賬戶餘額
分析:
共用資源是什嗎?顯然是一個帳號。
是否需要用多線程?顯然是用的,因為有兩個儲戶
1 class Account { 2 double balance = 0; 3 public synchronized void cunqian(double crm){ 4 balance += crm; 5 System.out.println(Thread.currentThread().getName() + ":" + balance); 6 } 7 } 8 class Customer implements Runnable{ 9 Account account;10 public Customer(Account account) {11 this.account = account;12 }13 @Override14 public void run() {15 for (int i = 0; i < 3; i++) {16 account.cunqian(1000);17 }18 }19 }20 public static void main(String[] args) throws Exception {21 Account account = new Account();22 Customer customer = new Customer(account);23 Thread t1 = new Thread(customer);24 t1.start();25 Thread t2 = new Thread(customer);26 t2.start();27 t1.setName("儲戶1");28 t2.setName("儲戶2");29 }
上面代碼需要需要的是:由於是兩個類,一定要保證共用資源類千萬不要被多次執行個體化
所以一定要讓第一個類執行個體化完成後當成形參出入到第二個中構造(看紅色標記部分)
線程通訊:
① wait():令當前線程掛起並放棄CPU、同步資源。讓別的線程可訪問並修改共用資源,而當前前程排隊等候再次對資源的訪問
② notify():喚醒正在排隊等候同步資源的線程中優先順序最高的鎖
③ notifyAll();喚醒所有正在排隊的等待的所有線程結束等待
跟著剛哥梳理java知識點——多線程(十六)