1.1什麼是主線程:
java程式啟動時由系統建立的并執行main方法的線程則稱為主線程。
主線程的執行路徑:
從main方法開始直到main方法執行完畢。
什麼是子線程:
除主線程以外的其他所有線程都稱為子線程
子線程的執行路徑:
從run方法開始直到run方法執行完畢
1.2線程的運作模式
分時式模式:所有的線程輪流獲得CPU的使用權,并且平均配置設定每個線程占用的CPU時間。
搶占式模式:優先級高的線程先獲得CPU使用權的機率高,如果線程的優先級相同,會随機選擇一個線程來獲得CPU使用權。Java程式中的線程運作模式屬于該種模式。
1.3多線程原理和記憶體圖解
當執行線程的任務結束 了,線程自己在棧記憶體中釋放了。但是當所有的執行線程都結束了,那麼程序就結束了。
1.4Thread類常用方法
在上一天内容中我們已經可以完成最基本的線程開啟,那麼在我們完成操作過程中乃至了java.lang.Thread類,API中該類中定義了有關線程的一些方法,具體如下:
構造方法:
public Thread() :配置設定一個新的線程對象。
public Thread(String name) :配置設定一個帶有名字的新的線程對象
public Thread(Runnable target):配置設定一個帶有指定目标新的線程對象
public Thread(Runnable target,String name); 配置設定一個帶有指定目标的線程對象并指定名字。
常用方法:
public String getName():擷取目前線程名稱。
public void setName():擷取目前線程名稱。
public void start():開啟線程,在新的執行路徑中執行run方法。
public static void sleep(long millis):讓目前線程休眠指定的時間,機關:毫秒值(暫時停止執行)。
public static Thread currenThread():獲得執行目前方法的線程對象。
翻閱API後得知建立線程的方式總共有兩種,一種是繼承Thread類方式,一種是實作Runnable接口方式。
1.5建立線程方式二
采用java.lang.runnable也是非常常見的一種, 我們隻需要重寫run方法即可。
步驟如下:
- 定義Runnable接口的實作類,并重寫該接口的run方法,該run()方法的方法體同樣是該線程的線程執行體。
- 建立runnable實作類的執行個體,并以些執行個體作為Thread的target來建立Thread對象,該對象才是真正的線程對象。
- 調用線程對象的start()方法來啟動線程。
通過實作Runnable接口,使得該類有了多線程的特征。run()方法是多線程程式 的一個執行目标。所有的多線程代碼都在run方法裡面。Thread類實際上也是實作了Runnable接口的類。
在啟動的多線程的時候,需要先通過Thread的start()方法Thread(Runnable target)構造出對象,然後調用Thread對象的start()方法來運作多線程代碼。
實際上所有的多線程代碼都是通過運作Thread的start()方法來運作的。是以,不管是繼承Thread類還是實作Runnable接口來實作多線程,最終 還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程程式設計的基礎。
Tips:Runnable對象僅僅是作為Thread對象的target,Runnable實作類裡包含的run()方法僅作為線程執行體。而實際的線程對象依然是Thread執行個體,隻是該Thread線程負責執行其target的run()方法。
1.6Thread和Runnable的差別
如果一個類繼承Thread,則不适合資源共享。但不适合資源共享。但是如果實作了Runable接口的話,則很容易的實作資源共享。
總結
實觀 Runnable接口比繼承 Thread類所具有的優勢:
1.适合多個相同的程式代碼的線程去共享同一個資源。
2.可以避免java中的單繼承的局限性。
3.增加程式的健壯性,實現解耦操作,代碼可以被多個線程共享,代碼和資料獨立
4.線程池隻能放入實作 Runable或 callable類線程,不能直接放入繼承 Thread的類
擴充:在java中,每次程式運作至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java指令執行一個類的時候,突際上都會啟動一個JVM,每一個JVM其實在就是在作業系統中啟動了一個程序。
1.7匿名内部類方式實作線程的建立
使用線程的内匿名内部類方式,可以友善的實作每個線程執行不同的線程任務操作。
使用匿名内部類的方式實作 Runnable接目,重新 Runnable接目中的run方法。
2.1線程安全概念
如果有多個線程在同時運作,而這些線程可能會同時運作這段代碼。程式每次運作結果和單線程運作 的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。
簡單記憶:兩個或兩個以上的線程在通路共享資源時仍然能得到正确的結果則稱為線程安全。
線程安全問題都是由全局變量及靜态變量引起的。若每個線程中對全局變量、靜态變量隻有讀 操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作, 一般都需要考慮線程同步,否則的話就可能影響線程安全。
2.2線程同步
當我們使用多個線程通路統一資源的時候,且多個線程中對資源有寫的操作,就容易出現線程安全問題。
要解決上述多線程并發通路多一個資源的安全性問題:也就是解決重複票與不存在票問題,java中提 供了同步機制(synchronized)來解決。
在某個線程修改共享資源的時候,其他線程不能修改該資源,等待 修改完畢同步之後,才能去搶奪CPU資源,完成對應的操作,保證了資料的同步性,解決了線程不安全 的現象。
為了保證每個線程都能正常執行原子操作Java引入了線程同步機制。
那麼怎麼去使用呢?有三種方式完成同步操作:
- 同步代碼塊。
- 同步方法。
- 鎖機制。
2.3同步代碼塊
•同步代碼塊:synchronized關鍵字可以用于方法中的某個區塊中,表示隻對這個區塊的資源實行互斥通路。
■同步代碼格式
(synchronized 同步鎖){
需要同步操作的代碼
}
■同步鎖注意事項
鎖對象可以是任意類型。
多個線程對象要使用同一把鎖。
貼士:在任何時候,最多允許一個線程擁有同步鎖,誰拿到鎖就進入代碼塊,其他的線程隻能在 外等着。
•同步代碼塊實作線程安全
2.4同步方法
•同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執行該方法的時候,其他線
程隻能在方法外等着。
■同步方法格式
public synchronized void method(){
可能會産生線程安全問題的代碼
}
同步鎖是誰?
對于非static方法,同步鎖就是this。
對于static方法,我們使用目前方法所在類的位元組碼對象(類名.class)。
•同步方法實作線程安全
2.5 Lock 鎖
Lock鎖概述
java.util.concurrent.locks.Lock 機制提供了比synchronized代碼塊和synchronized方法更廣
泛的鎖定操作,同步代碼塊/同步方法具有的功能Lock都有,除此之外更強大,更展現面向對象。
Lock常用方法
• public void lock():加同步鎖。
• public void unlock():釋放同步鎖。
Lock鎖實作線程安全
線程狀态
3.1線程狀态概述
當線程被建立并啟動以後,它既不是一啟動就進入了執行狀态,也不是一直處于執行狀态。線上程的 生命周期中,有幾種狀态呢?在API中java.lang.Thread.State這個枚舉中給出了六種線程狀态:
這裡先列出各個線程狀态發生的條件,下面将會對每種狀态進行詳細解析
線程狀态 | 導緻狀态發生條件 |
NEW(建立) | 線程剛被建立,但是并未啟動。 |
Runnable(可 運作) | 線程可以在java虛拟機中運作的狀态,可能正在運作自己代碼,也可能沒 有,這取決于作業系統處理器。一個正在」ava虛拟機中執行的線程處于這 一狀态。 |
Blocked(鎖阻 塞) | 當一個線程試圖擷取一個對象鎖,而該對象鎖被其他的線程持有,則該線程 進入Blocked狀态;當該線程持有鎖時,該線程将變成Runnable狀态。 |
Waiting(無限 等待) | 一個線程在等待另一個線程執行一個(喚醒)動作時,該線程進入Waiting 狀态。進入這個狀态後是不能自動喚醒的,必須等待另一個線程調用notify 或者notifyAll方法才能夠喚醒。 |
Timed Waiting(計曰寸 等待) | 同waiting狀态,有幾個方法有逾時參數,調用他們将進入Timed Waiting狀 态。這一狀态将一直保持到逾時期滿或者接收到喚醒通知。帶有逾時參數的 常用方法有Thread.sleep、Object.wait。 |
Teminated(被 終止) | 因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了 run方法而死 亡。 |
我們不需要去研究這幾種狀态的實作原理,我們隻需知道在做線程操作中存在這樣的狀态。那我們怎 麼去了解這幾個狀态呢,建立與被終止還是很容易了解的,我們就研究一下線程從Runnable (可運 行)狀态與非運作狀态之間的轉換問題。