多線程(二)~Thread類相關的API介紹


 

一、線程安全問題:

    當我們使用多個線程操作統一方法內的局部變量的時候,每個局部變量在當前線程里都有自己的副本,這種情況是不會出現線程安全問題的。當我們兩個線程同時操作全局變量的時候,有可能會引發線程安全的問題。
 
①.業務類
  1. package com.multiThread.bean;
  2. publicclassAservise{
  3. privateString name;
  4. publicvoid doBusiness(String name){
  5. this.name = name;
  6. System.out.println("大家好,我是"+this.name);
  7. }
  8. }
②.線程類
  1. package com.multiThread.thread;
  2. import com.multiThread.bean.Aservise;
  3. publicclassUnSafeThreadimplementsRunnable{
  4. privateAservise aServise;
  5. privateString name;
  6. publicUnSafeThread(Aservise aServise,String name){
  7. this.aServise = aServise;
  8. this.name = name;
  9. }
  10. @Override
  11. publicvoid run(){
  12. aServise.doBusiness(this.name);
  13. }
  14. }
③.測試類
  1. package com.multiThread.test.common;
  2. import com.multiThread.bean.Aservise;
  3. import com.multiThread.thread.UnSafeThread;
  4. publicclassUnSafeThreadTest{
  5. publicstaticvoid main(String[] args){
  6. Aservise aService =newAservise();
  7. UnSafeThread unSafeThreadZhang =newUnSafeThread(aService,"張三");
  8. UnSafeThread unSafeThreadLi =newUnSafeThread(aService,"李四");
  9. Thread zhang =newThread(unSafeThreadZhang);
  10. Thread li =newThread(unSafeThreadLi);
  11. zhang.start();
  12. li.start();
  13. }
  14. }
預期輸出結果
  1. 大家好,我是張三
  2. 大家好,我是李四
多次運行實際輸出結果
  1. 大家好,我是李四
  2. 大家好,我是李四
  3. 大家好,我是張三
  4. 大家好,我是李四
  5. 大家好,我是張三
  6. 大家好,我是張三
這個例子很好的解釋了多個線程同時操作全局變量會存在線程安全的問題。
那么這種問題該如何解決呢?在這里我們只討論服務器為單節點的情況,不考慮集群模式。
解決的方式是加鎖,只要保證這兩段程序不同時變更全局變量就OK。
 
 
將業務類的doBusiness方法更改為使用synchronized關鍵字修飾就可以:
1.在方法上聲明,給當前對象加鎖(非靜態方法):
  1. publicsynchronizedvoid doBusiness(String name){
  2. this.name = name;
  3. System.out.println("大家好,我是"+this.name);
  4. }
 
2.synchronized不僅僅可以在方法上聲明,也可以在方法內部聲明(參數是Object類型的值,寫成this代表給當前對象上鎖)。這種情況叫做同步代碼塊:
  1. publicvoid doBusiness(String name){
  2. synchronized(this){
  3. this.name = name;
  4. System.out.println("大家好,我是"+this.name);
  5. }
  6. }
對象監視器:
    synchronize修飾方法或者synchronized(this),對象監視器監視當前類的對象。
    synchronize(其他對象),對象監視器給其他對象上鎖。
需要注意的問題:
    一般不會使用字符串作為監視對象,因為字符串有個常量池的概念,在不同的地方操作可能鎖的是同一個對象。
synchronize作用:
    1.給對象監視器監視的對象上鎖
    2.保證同一時間只有一個對象可以獲得此對象監視器上的鎖。
 
注意:
    ①.synchronize關鍵字如果在非靜態方法上聲明或者在非靜態代碼塊上聲明synchronized(this),代表給 當前對象上鎖,當一個線程獲得此鎖的同時,其他線程調用synchronize修飾的方法、代碼塊均需等待。當此線程執行完畢或者主動釋放鎖時,根據cpu調度看哪個線程能獲得此鎖。
    ②.如果synchronize(其他對象),則代表給參數中的對象上鎖,所有參數對象共用同一把鎖, 只有獲得鎖的線程可以執行此代碼塊。
    ③.如果是靜態方法,則代表是給整個類上鎖,那么整個類共用同一把鎖,只有獲得鎖的線程可以執行同步方法、代碼塊。
 
使用jstack查看死鎖:
    
    ①.cd到jdk/bin目錄下,執行jps命令,得到正在運行的進程id,這塊我筆記本上運行不出來。我也不知道哪個是進程id。
    ②.執行jstack - l 進程id
    ③.得到jstack后分析一下程序哪個地方設計有bug,修改程序。
 
二、Thread類相關API操作:
    
好了,現在我們已經對多線程和線程安全有了一定的認識。下面我們通過具體代碼來了解一下java.lang.Thread類相關的API的常用操作。
 
currentThread():獲取當前執行當前線程的線程對象
isAlive():當前線程是否存活(正在執行)
sleep():使當前線程睡眠一段時間,參數單位毫秒
getId():獲取當前線程的唯一標識
yield():釋放當前線程的控制權,將控制權交由CPU調度。
interrupt():停止線程執行( 只調用這個方法無法中止線程的執行,需要配合interrupted()才能中斷線程。線程在sleep的情況下中斷會拋異常並且清除停止狀態的值,使之變為false
interrupted():測試 當前線程是否已經是中斷狀態,執行后具有將狀態標志清除為false的功能。
                              此方法多次調用會有問題。比如第一次中斷線程,再次調用則會清除中斷狀態。
isInterrupted():測試線程是否已中斷。
setPriority ():設置線程的優先級,優先級越高,優先執行的幾率越大。取值范圍是1~10
 
jdk中不推薦使用的 過期的方法以及原因
stop():強制中斷當前線程。 強制停止某個線程可能會造成某些清理操作無法完成。而且會將對象的鎖給清除掉,有可能會造成數據不一致的問題。
suspend():暫停當前線程的執行, 此操作並不會釋放鎖,有鎖獨占的問題。
                    另外如果在System.out.println()中暫停了線程,同步鎖並未釋放。其他有System.out.println()的地方就無法執行。
                    因為Sysem.out.println()本身底層的實現也是基於synchronized的。一個對象占着鎖不放,這邊就一直得不到執行。
System.out.println()的實現:
  1. publicvoid println(String x){
  2. synchronized(this){
  3. print(x);
  4. newLine();
  5. }
  6. }
resume() :恢復當前線程的執行, 此操作並不會釋放鎖,有鎖獨占的問題。
 
    






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM