1.1 多線程原理
昨天的時候我們已經寫過一版多線程的代碼,很多同學對原理不是很清楚,那麼我們今天先畫個多線程執行時序圖
來展現一下多線程程式的執行流程。
代碼如下:
自定義線程類:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yM2gTNxgDN4kTZkRmY2ADNzYzXxQTMxIDM4AzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
測試類:
流程圖:
程式啟動運作 main 時候,
java
虛拟機啟動一個程序,主線程
main
在
main()
調用時候被建立。随着調用
mt
的對象的
start 方法,另外一個新的線程也啟動了,這樣,整個應用就在多線程下運作。
通過這張圖我們可以很清晰的看到多線程的執行流程,那麼為什麼可以完成并發執行呢?我們再來講一講原理。
多線程執行時,到底在記憶體中是如何運作的呢?以上個程式為例,進行圖解說明:
多線程執行時,在棧記憶體中,其實 每一個執行線程都有一片自己所屬的棧記憶體空間
。進行方法的壓棧和彈棧
當執行線程的任務結束了,線程自動在棧記憶體中釋放了。但是當所有的執行線程都結束了,那麼程序就結束了
1.2 Thread類
在上一天内容中我們已經可以完成最基本的線程開啟,那麼在我們完成操作過程中用到了 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 start() : 導緻此線程開始執行 ; Java
虛拟機調用此線程的
run
方法。
public void run() : 此線程要執行的任務在此處定義代碼。
public static void sleep(long millis) : 使目前正在執行的線程以指定的毫秒數暫停(暫時停止執行)。
public static Thread currentThread() : 傳回對目前正在執行的線程對象的引用。
翻閱 API 後得知建立線程的方式總共有兩種,一種是繼承 Thread
類方式,一種是實作
Runnable
接口方式,方式一我
們上一天已經完成,接下來講解方式二實作的方式。
1.3 建立線程方式二
采用 java.lang.Runnable 也是非常常見的一種,我們隻需要重寫 run
方法即可。
步驟如下:
1. 定義 Runnable 接口的實作類,并重寫該接口的
run()
方法,該
run()
方法的方法體同樣是該線程的線程執行體。
2. 建立 Runnable 實作類的執行個體,并以此執行個體作為
Thread
的
target
來建立
Thread
對象,該
Thread
對象才是真正
的線程對象。
3. 調用線程對象的 start() 方法來啟動線程。
代碼如下:
通過實作 Runnable 接口,使得該類有了多線程類的特征。 run() 方法是多線程程式的一個執行目标。所有的多線程
代碼都在 run 方法裡面。 Thread 類實際上也是實作了
Runnable
接口的類。
在啟動的多線程的時候,需要先通過 Thread 類的構造方法 Thread(Runnable target) 構造出對象,然後調用
Thread
對象的 start() 方法來運作多線程代碼。
實際上所有的多線程代碼都是通過運作 Thread 的 start() 方法來運作的。是以,不管是繼承
Thread
類還是實作
Runnable 接口來實作多線程,最終還是通過 Thread 的對象的 API
來控制線程的,熟悉
Thread
類的
API
是進行多線程
程式設計的基礎。
tips:Runnable 對象僅僅作為 Thread 對象的 target
,
Runnable
實作類裡包含的
run()
方法僅作為線程執行體。
而實際的線程對象依然是 Thread 執行個體,隻是該 Thread 線程負責執行其
target
的
run()
方法。
1.4 Thread和Runnable的差別
如果一個類繼承 Thread ,則不适合資源共享。但是如果實作了 Runable 接口的話,則很容易的實作資源共享。
總結:
實作
Runnable
接口比繼承
Thread
類所具有的優勢:
1. 适合多個相同的程式代碼的線程去共享同一個資源。
2. 可以避免 java 中的單繼承的局限性。
3. 增加程式的健壯性,實作解耦操作,代碼可以被多個線程共享,代碼和線程獨立。
4. 線程池隻能放入實作 Runable 或 Callable
類線程,不能直接放入繼承
Thread
的類。
擴充:在 java 中,每次程式運作至少啟動 2 個線程。一個是
main
線程,一個是垃圾收集線程。因為每當使用
java 指令執行一個類的時候,實際上都會啟動一個 JVM ,每一個 JVM
其實在就是在作業系統中啟動了一個進
程。
1.5 匿名内部類方式實作線程的建立
使用線程的内匿名内部類方式,可以友善的實作每個線程執行不同的線程任務操作。
使用匿名内部類的方式實作 Runnable 接口,重新 Runnable 接口中的run方法: