Java面向對象之多線程運行機制

Java較爲實用的多線程設計與應用

多線程機制討論


電梯調度算法

請求分配順序html

自動化測試方法

  • 生成隨機數據
  • 驗證輸出數據正確性
  • 打包本身的代碼文件
  • 運行評測機

線程安全與鎖:https://www.cnblogs.com/zhaojinhui/p/5526988.html

  • lock
  • condition
  • reentrantlock
  • Semaohrone信號量

代碼耦合性和內聚性

  同一個模塊內的各個元素之間要高度緊密,可是各個模塊之間的相互依存度卻要不那麼緊密。 內聚和耦合是密切相關的,同其餘模塊存在高耦合的模塊意味着低內聚,而高內聚的模塊意味着該模塊同其餘模塊之間是低耦合。在進行軟件設計時,應力爭作到高內聚,低耦合。java

下降耦合

  一個模塊訪問另外一個模塊的內容; 模塊間傳遞數據結構爲而不是單一數據算法

加強內聚

  邏輯上獨立模塊; 時間上同時執行的語句能夠組合; 模塊的元素只與該模塊功能相關,模塊之間按照順序使用其輸出結果做爲輸入結果安全

多線程介紹

更多1.http://www.javashuo.com/article/p-mwdqdxmu-a.html數據結構

2.http://www.cnblogs.com/xingele0917/p/3623162.html多線程

  Java提供了線程類Thread來建立多線程的程序。其實,建立線程與建立普通的類的對象的操做是同樣的,而線程就是Thread類或其子類的實例對象。每一個Thread對象描述了一個單獨的線程。要產生一個線程,有兩種方法:測試

◆須要從Java.lang.Thread類派生一個新的線程類,重載它的run()方法; 
◆實現Runnalbe接口,重載Runnalbe接口中的run()方法。優化

  爲何Java要提供兩種方法來建立線程呢?它們都有哪些區別?相比而言,哪種方法更好呢?ui

  在Java中,類僅支持單繼承,也就是說,當定義一個新的類的時候,它只能擴展一個外部類.這樣,若是建立自定義線程類的時候是經過擴展 Thread類的方法來實現的,那麼這個自定義類就不能再去擴展其餘的類,也就沒法實現更加複雜的功能。所以,若是自定義類必須擴展其餘的類,那麼就可使用實現Runnable接口的方法來定義該類爲線程類,這樣就能夠避免Java單繼承所帶來的侷限性。this

  還有一點最重要的就是使用實現Runnable接口的方式建立的線程能夠處理同一資源,從而實現資源的共享.

   線程的狀態:

  • 建立:已經有Thread實例了, 可是CPU還有爲其分配資源和時間片。
  • 就緒:線程已經得到了運行所需的全部資源,只等CPU進行時間調度。
  • 運行:線程位於當前CPU時間片中,正在執行相關邏輯。
  • 休眠:通常是調用Thread.sleep後的狀態,這時線程依然持有運行所需的各類資源,可是不會被CPU調度。
  • 掛起:通常是調用Thread.suspend後的狀態,和休眠相似,CPU不會調度該線程,不一樣的是,這種狀態下,線程會釋放全部資源。
  • 死亡:線程運行結束或者調用了Thread.stop方法。

 建立並運行線程:

  • Thread()或者Thread(Runnable):構造線程。
  • Thread.start:啓動線程。
  • Thread.sleep:將線程切換至休眠狀態。
  • Thread.interrupt:中斷線程的執行。
  • Thread.join:等待某線程結束。
  • Thread.yield:剝奪線程在CPU上的執行時間片,等待下一次調度。
  • Object.wait:將Object上全部線程鎖定,直到notify方法才繼續運行。
  • Object.notify:隨機喚醒Object上的1個線程。
  • Object.notifyAll:喚醒Object上的全部線程。

 

線程安全與鎖

更多1.http://www.javashuo.com/article/p-hmeqidde-n.html

  2.http://www.javashuo.com/article/p-ccpcxioi-bo.html    

  3.http://www.javashuo.com/article/p-omnlcwtn-cb.html

  因爲考慮到多個線程同時搶佔資源時,會發生衝突,這叫作線程安全問題,即咱們須要避免多個線程同時訪問或修改同一資源,其實現機制爲同步與互斥。避免衝突的方法就是對臨界資源或者臨界區加鎖。

鎖方法:

1.同步方法

1 在方法聲明上加上synchronized 2 
3 public synchronized void method() { 4  可能會產生線程安全問題的代碼 5 }  

  同步方法中鎖的對象是 this(即調用者對象),並不必定是方法中用到的對象數據!

2.靜態同步方法

1 在方法聲明上加上static synchronized
2 
3 public static synchronized void method() {
4     可能會產生線程安全問題的代碼
5 }

  靜態同步方法中的鎖對象是 類名.class(由於在加載類文件的時候,靜態同步方法因爲是靜態的也被加載進內存了,類名.class的加載優先級高於靜態方法)

3.同步代碼塊

1 在須要同步的代碼外面包上一個synchronized
2 
3 synchronized(Object o) {
4     可能會產生線程安全問題的代碼
5 } 

  同步代碼塊中鎖的對象能夠是任意對象(即Object o)

鎖對象:

  this指當前調用者對象,通常狀況下,普通方法塊中是省略的(在構造方法中沒有省略)。

死鎖: 

   當前線程正在訪問共享對象時,因爲一個肯定的條件在某狀況下不會發生,則該線程不釋放鎖,那麼其他線程沒法得到鎖,也沒法運行線程,多線程就會一直陷入等待中,等待這個「永遠不會發生」的條件,這就會形成死鎖。

原語操做和其餘鎖:

   經常使用的原語有信號量P/V操做、管程、共享內存等,是進程之間經常使用的通訊的方式。具體參考操做系統或原語、信號量。


 

synchronized是不錯,但它並不完美。它有一些功能性的限制:

  1. 它沒法中斷一個正在等候得到鎖的線程;
  2. 也沒有有權利獲得鎖,只能一直陷入等待;即便不想等待,也就無法獲得鎖;
  3. 同步還要求鎖的釋放只能在與得到鎖所在的堆棧幀相同的堆棧幀中進行,多數狀況下,這沒問題(並且與異常處理交互得很好),可是,確實存在一些非塊結構的鎖定更合適的狀況。

  獲取鎖的線程釋放鎖只會有兩種狀況:

  一、獲取鎖的線程執行完了該代碼塊,而後線程釋放對鎖的佔有。

  二、線程執行發生異常,此時JVM會讓線程自動釋放鎖。


 

  其餘鎖

Lock與synchronized對比

  一、Lock不是Java語言內置的,synchronized是Java語言的關鍵字,所以是內置特性。Lock是一個類,經過這個類能夠實現同步訪問。

  二、synchronized不須要手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完以後,系統會自動讓線程釋放對鎖的佔用;而Lock則必需要用戶去手動釋放鎖,若是沒有主動釋放鎖,就有可能致使出現死鎖現象

因此lock是能夠手動釋放鎖的。


 java.util.concurrent.locks包中經常使用的類和接口:

 1 public interface Lock {
 2     //用來獲取鎖。若是鎖已被其餘線程獲取,則進行等待。
 3     void lock();
 4    // 當經過這個方法去獲取鎖時,若是線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態
 5     void lockInterruptibly() throws InterruptedException;
 6     //它表示用來嘗試獲取鎖,若是獲取成功,則返回true,若是獲取失敗(即鎖已被其餘線程獲取),則返回false
 7     boolean tryLock();
 8     /*與tryLock()方法是相似的,只不過區別在於這個方法在拿不到鎖時會等待必定的時間,在時間期限以內若是還拿不到鎖,就返回false。
    若是若是一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。*/
9 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 10 //釋放鎖 11 void unlock(); 12 Condition newCondition(); 13 }

  一、Lock與unlock

  Lock用於獲取鎖,但lock不會主動釋放鎖(調用unlock釋放),因此須要與unlock()配合使用。通常在使用Lock時必須在try{}catch{}塊中進行,而且將釋放鎖的操做放在finally塊中進行,以保證鎖必定被被釋放,防止死鎖的發生。

    PS:同一個線程能夠連續得到同一把鎖,但也必須釋放相同次數的鎖。

二、獲取鎖等待時間tryLock(long time, TimeUnit unit)

  若是你和朋友有約,在約定時間內對方未出現,我想你確定會掃興的離去。對於線程來講也應該時這樣的,由於一般咱們是沒法判斷一個線程爲何會沒法得到鎖,但咱們能夠給該線程一個獲取鎖的時間限制,若是到時間尚未獲取到鎖,則放棄獲取鎖。


ReentrantLock增長了鎖:

    1. void lock(); // 無條件的鎖;

    2. void lockInterruptibly throws InterruptedException;//可中斷的鎖;

  解釋: 使用ReentrantLock若是獲取了鎖當即返回,若是沒有獲取鎖,當前線程處於休眠狀態,直到得到鎖或者當前線程能夠被別的線程中斷去作其餘的事情;可是若是是synchronized的話,若是沒有獲取到鎖,則會一直等待下去;

    3. boolean tryLock();//若是獲取了鎖當即返回true,若是別的線程正持有,當即返回false,不會等待;

  4. boolean tryLock(long timeout,TimeUnit unit);//若是獲取了鎖當即返回true,若是別的線程正持有鎖,會等待參數給的時間,在等待的過程當中,若是獲取鎖,則返回true,若是等待超時,返回false。


Condition的特性:

  1.Condition中的await()方法至關於Object的wait()方法,Condition中的signal()方法至關於Object的notify()方法,Condition中的signalAll()至關於Object的notifyAll()方法。不一樣的是,Object中的這些方法是和同步鎖捆綁使用的;而Condition是須要與互斥鎖/共享鎖捆綁使用的。

  2.Condition它更強大的地方在於:可以更加精細的控制多線程的休眠與喚醒。對於同一個鎖,咱們能夠建立多個Condition,在不一樣的狀況下使用不一樣的Condition。
  例如,假如多線程讀/寫同一個緩衝區:當向緩衝區中寫入數據以後,喚醒"讀線程";當從緩衝區讀出數據以後,喚醒"寫線程";而且當緩衝區滿的時候,"寫線程"須要等待;當緩衝區爲空時,"讀線程"須要等待。      

  若是採用Object類中的wait(), notify(), notifyAll()實現該緩衝區,當向緩衝區寫入數據以後須要喚醒"讀線程"時,不可能經過notify()或notifyAll()明確的指定喚醒"讀線程",而只能經過notifyAll喚醒全部線程(可是notifyAll沒法區分喚醒的線程是讀線程,仍是寫線程)。  可是,經過Condition,就能明確的指定喚醒讀線程。 

 


 

線程調度

更多1.http://www.javashuo.com/article/p-tecfgclf-bz.html

2.http://www.javashuo.com/article/p-mwdqdxmu-a.html

  線程建立後,就要將它們運行起來,然而怎樣調度怎樣分配CPU給線程是一個問題。 

調度策略

  通常線程調度模式分爲兩種——搶佔式調度和協同式調度。搶佔式調度指的是每條線程執行的時間、線程的切換都由系統控制,系統控制指的是在系統某種運行機制下,可能每條線程都分一樣的執行時間片,也多是某些線程執行的時間片較長,甚至某些線程得不到執行的時間片。在這種機制下,一個線程的堵塞不會致使整個進程堵塞。協同式調度指某一線程執行完後主動通知系統切換到另外一線程上執行,這種模式就像接力賽同樣,一我的跑完本身的路程就把接力棒交接給下一我的,下我的繼續往下跑。線程的執行時間由線程自己控制,線程切換能夠預知,不存在多線程同步問題,但它有一個致命弱點:若是一個線程編寫有問題,運行到一半就一直堵塞,那麼可能致使整個系統崩潰。
---------------------
做者:超人汪小建(seaboat)
來源:CSDN
原文:https://blog.csdn.net/wangyangzhizhou/article/details/41122385

  但實際上咱們有時須要手動進行調整,就是按照自個人意願去改變線程調度方法,讓某一個線程具備優先權,每當該線程的條件知足就中止其它線程,而運行該線程。

 

調度優化及線程優先級

  優先級在各類常見算法中並很多見,咱們調度多線程能夠利用優先級來肯定誰先誰後(操做系統中進程調度也有優先級調度方法)。理所固然,優先級高的線程會搶佔CPU運行。

1 Java線程的優先級用整數表示,取值範圍是1~10,Thread類有如下三個靜態常量:
2 static int MAX_PRIORITY
3           線程能夠具備的最高優先級,取值爲10。
4 static int MIN_PRIORITY
5           線程能夠具備的最低優先級,取值爲1。
6 static int NORM_PRIORITY
7           分配給線程的默認優先級,取值爲5。
   Thread類的setPriority()和getPriority()方法分別用來設置和獲取線程的優先級。
  每一個線程都有默認的優先級。主線程的默認優先級爲Thread.NORM_PRIORITY。線程的優先級有繼承關係,好比A線程中建立了B線程,那麼B將和A具備相同的優先級。JVM提供了10個線程優先級,但與常見的操做系統都不能很好的映射。若是但願程序能移植到各個操做系統中,應該僅僅使用Thread類有如下三個靜態常量做爲優先級,這樣能保證一樣的優先級採用了一樣的調度方式。

線程切換

  線程睡眠:Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒爲單位。當睡眠結束後,就轉爲就緒(Runnable)狀態。sleep()平臺移植性好。

 
  線程等待:Object類中的wait()方法,致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。這個兩個喚醒方法也是Object類中的方法,行爲等價於調用 wait(0) 同樣。
 
  線程讓步:Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先級的線程。
 
  線程加入:join()方法,等待其餘線程終止。在當前線程中調用另外一個線程的join()方法,則當前線程轉入阻塞狀態,直到另外一個進程運行結束,當前線程再由阻塞轉爲就緒狀態。
 
  線程喚醒:Object類中的notify()方法,喚醒在此對象監視器上等待的單個線程。若是全部線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,並在對實現作出決定時發生。線程經過調用其中一個 wait 方法,在對象的監視器上等待。 直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其餘全部線程進行競爭;例如,喚醒的線程在做爲鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。相似的方法還有一個notifyAll(),喚醒在此對象監視器上等待的全部線程。

 

線程等待與喚醒

更多1.http://www.javashuo.com/article/p-tecfgclf-bz.html

2.http://www.javashuo.com/article/p-mwdqdxmu-a.html

wait與sleep 

  共同點:

1. 他們都是在多線程的環境下,均可以在程序的調用處阻塞指定的毫秒數,並返回。 
2. wait()和sleep()均可以經過interrupt()方法 打斷線程的暫停狀態 ,從而使線程馬上拋出InterruptedException。 
   若是線程A但願當即結束線程B,則能夠對線程B對應的Thread實例調用interrupt方法。若是此刻線程B正在wait/sleep /join,則線程B會馬上拋出InterruptedException,在catch() {} 中直接return便可安全地結束線程。 
   須要注意的是,InterruptedException是線程本身從內部拋出的,並非interrupt()方法拋出的。對某一線程調用 interrupt()時,若是該線程正在執行普通的代碼,那麼該線程根本就不會拋出InterruptedException。可是,一旦該線程進入到 wait()/sleep()/join()後,就會馬上拋出InterruptedException 。 

不一樣點:

  1.每一個對象都有一個鎖來控制同步訪問。Synchronized關鍵字能夠和對象的鎖交互,來實現線程的同步。sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或者方法。

  2. wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用 

  3. sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常

    因此sleep()和wait()方法的最大區別是:

    sleep()睡眠時,保持對象鎖,仍然佔有該鎖;

    而wait()睡眠時,釋放對象鎖。 

  sleep()使當前線程進入停滯狀態(阻塞當前線程),讓出CUP的使用、目的是不讓當前線程獨自霸佔該進程所獲的CPU資源,以留必定時間給其餘線程執行的機會;sleep()是Thread類的Static(靜態)的方法;所以他不能改變對象的機鎖,因此當在一個Synchronized塊中調用Sleep()方法是,線程雖然休眠了,可是對象的機鎖並木有被釋放,其餘線程沒法訪問這個對象(即便睡着也持有對象鎖)。

  在sleep()休眠時間期滿後,該線程不必定會當即執行,這是由於其它線程可能正在運行並且沒有被調度爲放棄執行,除非此線程具備更高的優先級。 
wait()方法是Object類裏的方法;當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖(暫時失去機鎖,wait(long timeout)超時時間到後還須要返還對象鎖);其餘線程能夠訪問;
  wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的線程。
  wiat()必須放在synchronized block中,不然會在program runtime時扔出」java.lang.IllegalMonitorStateException「異常。

notify與notifyAll

   notify()只能喚醒一個在等待該對象(鎖住的對象)線程,而notifyAll()喚醒全部在等待該對象的線程。

   Obj.wait(),與Obj.notify()/Obj.notifyAll()必需要與synchronized(Obj)一塊兒使用,也就是wait,與notify是針對已經獲取了Obj鎖進行操做,從語法角度來講就是Obj.wait(),Obj.notify必須在synchronized(Obj){...}語句塊內。從功能上來講wait就是說線程在獲取對象鎖後,主動釋放對象鎖,同時本線程休眠。直到有其它線程調用對象的notify()喚醒該線程,才能繼續獲取對象鎖,並繼續執行。相應的notify()就是對對象鎖的喚醒操做。但有一點須要注意的是notify()調用後,並非立刻就釋放對象鎖的,而是在相應的synchronized(){}語句塊執行結束,自動釋放鎖後,JVM會在wait()對象鎖的線程中隨機選取一線程,賦予其對象鎖,喚醒線程,繼續執行。這樣就提供了在線程間同步、喚醒的操做。

相關文章
相關標籤/搜索