程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> [JAVA · 初級]:21.多線程

[JAVA · 初級]:21.多線程

編輯:JAVA綜合教程

[JAVA · 初級]:21.多線程


概述

程序:Program,是一個靜態的概念

進程:Process,是一個動態的概念

進程是程序的一次動態執行過程, 占用特定的地址空間.

每個進程都是獨立的,由3部分組成cpu,data,code

缺點:內存的浪費,cpu的負擔

線程:Thread,是進程中一個“單一的連續控制流程” (a single sequential flow ofcontrol)/執行路徑

線程又被稱為輕量級進程(lightweight process)。

Threads run at the same time, independentlyof one another

一個進程可擁有多個並行的(concurrent)線程

一個進程中的線程共享相同的內存單元/內存地址空間可以訪問相同的變量和對象,而且它們從同一堆中分配對象通信、數據交換、同步操作

由於線程間的通信是在同一地址空間上進行的,所以不需要額外的通信機制,這就使得通信更簡便而且信息傳遞的速度也更快。

\

 

實現

 

在Java中負責線程的這個功能的是Java.lang.Thread 這個類

可以通過創建 Thread 的實例來創建新的線程。

每個線程都是通過某個特定Thread對象所對應的方法run( )來完成其操作的,方法run( )稱為線程體。

通過調用Thead類的start()方法來啟動一個線程。

繼承Thread類方式的缺點:那就是如果我們的類已經從一個類繼承(如小程序必須繼承自 Applet 類),則無法再繼承 Thread 類

通過Runnable接口實現多線程

優點:可以同時實現繼承。實現Runnable接口方式要通用一些。

Java提供一個線程調度器來監控程序中啟動後進入就緒狀態的所有線程。線程調度器按照線程的優先級決定應調度哪個線程來執行。

線程的優先級用數字表示,范圍從1到10

一個線程的缺省優先級是5

Thread.MIN_PRIORITY = 1

Thread.MAX_PRIORITY = 10

Thread.NORM_PRIORITY = 5

使用下述線方法獲得或設置線程對象的優先級。

intgetPriority();

voidsetPriority(int newPriority);

注意:優先級低只是意味著獲得調度的概率低。並不是絕對先調用優先級高後調用優先級低的線程。

Demo

 

public class TestThread2 implementsRunnable {
         publicstatic void main(String[] args) {
                   Threadthread1 = new Thread(new TestThread2(1));
Thread thread2 = new Thread(newTestThread2(2));
t1.setPriority(1);
                   t2.setPriority(10);
thread1.start();
                   thread2.start();
         }
         publicvoid run() {
                   for(inti=0;i<100;i++){
                            System.out.println(this.threadId+":"+i);
                   }
         }
}

 

狀態

新生狀態
用new關鍵字和Thread類或其子類建立一個線程對象後,該線程對象就處於新生狀態。處於新生狀態的線程有自己的內存空間,通過調用start方法進入就緒狀態(runnable)

就緒狀態
處於就緒狀態的線程已經具備了運行條件,但還沒有分配到CPU,處於線程就緒隊列,等待系統為其分配CPU。等待狀態並不是執行狀態,當系統選定一個等待執行的Thread對象後,它就會從等待執行狀態進入執行狀態,系統挑選的動作稱之為“cpu調度”。一旦獲得CPU,線程就進入運行狀態並自動調用自己的run方法。

運行狀態
在運行狀態的線程執行自己的run方法中代碼,直到調用其他方法而終止、或等待某資源而阻塞或完成任務而死亡。如果在給定的時間片內沒有執行結束,就會被系統給換下來回到等待執行狀態。

阻塞狀態
處於運行狀態的線程在某些情況下,如執行了sleep(睡眠)方法,或等待I/O設備等資源,將讓出CPU並暫時停止自己的運行,進入阻塞狀態。 在阻塞狀態的線程不能進入就緒隊列。只有當引起阻塞的原因消除時,如睡眠時間已到,或等待的I/O設備空閒下來,線程便轉入就緒狀態,重新到就緒隊列中排隊等待,被系統選中後從原來停止的位置開始繼續運行。

死亡狀態
死亡狀態是線程生命周期中的最後一個階段。線程死亡的原因有兩個。一個是正常運行的線程完成了它的全部工作;另一個是線程被強制性地終止,如通過執行stop或destroy方法來終止一個線程[不推薦使用這兩個方法。前者會產生異常,後者是強制終止,不會釋放鎖。

\

同步與死鎖

 

由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。

由於我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。

所謂死鎖,是指多個進程循環等待它方占有的資源而無限期地僵持下去的局面。很顯然,如果沒有外力的作用,那麽死鎖涉及到的各個進程都將永遠處於封鎖狀態。從上面的例子可以看出,計算機系統產生死鎖的根本原因就是資源有限且操作不當。即:一種原因是系統提供的資源太少了,遠不能滿足並發進程對資源的需求。這種競爭資源引起的死鎖是我們要討論的核心。例如:消息是一種臨時性資源。某一時刻,進程A等待進程B發來的消息,進程B等待進程C發來的消息,而進程C又等待進程A發來的消息。消息未到,A,B,C三個進程均無法向前推進,也會發生進程通信上的死鎖。另一種原因是由於進程推進順序不合適引發的死鎖。資源少也未必一定產生死鎖。就如同兩個人過獨木橋,如果兩個人都要先過,在獨木橋上僵持不肯後退,必然會應競爭資源產生死鎖;但是,如果兩個人上橋前先看一看有無對方的人在橋上,當無對方的人在橋上時自己才上橋,那麽問題就解決了。所以,如果程序設計得不合理,造成進程推進的順序不當,也會出現死鎖。

死鎖的恢復

一旦在死鎖檢測時發現了死鎖,就要消除死鎖,使系統從死鎖狀態中恢復過來。

(1)最簡單,最常用的方法就是進行系統的重新啟動,不過這種方法代價很大,它意味著在這之前所有的進程已經完成的計算工作都將付之東流,包括參與死鎖的那些進程,以及未參與死鎖的進程。

(2)撤消進程,剝奪資源。終止參與死鎖的進程,收回它們占有的資源,從而解除死鎖。這時又分兩種情況:一次性撤消參與死鎖的全部進程,剝奪全部資源;或者逐步撤消參與死鎖的進程,逐步收回死鎖進程占有的資源。一般來說,選擇逐步撤消的進程時要按照一定的原則進行,目的是撤消那些代價最小的進程,比如按進程的優先級確定進程的代價;考慮進程運行時的代價和與此進程相關的外部作業的代價等因素。

此外,還有進程回退策略,即讓參與死鎖的進程回退到沒有發生死鎖前某一點處,並由此點處繼續執行,以求再次執行時不再發生死鎖。雖然這是個較理想的辦法,但是操作起來系統開銷極大,要有堆棧這樣的機構記錄進程的每一步變化,以便今後的回退,有時這是無法做到的。

任務調度

Timer定時器類

TimerTask任務類

通過java timer timetask:(spring的任務調度就是通過他們來實現的)

在這種實現方式中,Timer類實現的是類似鬧鐘的功能,也就是定時或者每隔一定時間觸發一次線程。其實,Timer類本身實現的就是一個線程,只是這個線程是用來實現調用其它線程的。而TimerTask類是一個抽象類,該類實現了Runnable接口,所以按照前面的介紹,該類具備多線程的能力。

在這種實現方式中,通過繼承TimerTask使該類獲得多線程的能力,將需要多線程執行的代碼書寫在run方法內部,然後通過Timer類啟動線程的執行。

在實際使用時,一個Timer可以啟動任意多個TimerTask實現的線程,但是多個線程之間會存在阻塞。所以如果多個線程之間如果需要完全獨立運行的話,最好還是一個Timer啟動一個TimerTask實現。

業務思想

多線程機制操作,獨立指令流並發執行,使得用戶很輕松。

在此講述了 Java 多線程編程的方方面面,包括創建線程,以及對多個線程進行調度、管理。深刻認識到了多線程編程的復雜性,以及線程切換開銷帶來的多線程程序的低效性,這也促使我們認真地思考一個問題:我們是否需要多線程?何時需要多線程?  

多線程的核心在於多個代碼塊並發執行,本質特點在於各代碼塊之間的代碼是亂序執行的。我們的程序是否需要多線程,就是要看這是否也是它的內在特點。  

假如我們的程序根本不要求多個代碼塊並發執行,那自然不需要使用多線程;假如我們的程序雖然要求多個代碼塊並發執行,但是卻不要求亂序,則我們完全可以用一個循環來簡單高效地實現,也不需要使用多線程;只有當它完全符合多線程的特點時,多線程機制對線程間通信和線程管理的強大支持才能有用武之地,這時使用多線程才是值得的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved