在Java中實(shí)現(xiàn)讓線程按照自己指定的順序執(zhí)行
如何讓線程按照自己指定的順序執(zhí)行
我們在日常的多線程開發(fā)中,可能有時(shí)會想讓每個(gè)線程都按照我們指定的順序來運(yùn)行,而不是讓CPU隨機(jī)調(diào)度,這樣可能會讓我們在日常的開發(fā)工作中帶來不必要的麻煩。
既然有了這個(gè)需求,也就引入了本文的標(biāo)題,讓線程按照自己指定的順序來運(yùn)行。
有興趣的同學(xué)可以猜想下列代碼可能運(yùn)行的結(jié)果:

按照正常的理解思路,上面代碼的執(zhí)行順序依次應(yīng)該為:t1 → t2 → t3,而實(shí)際效果則不是理想的狀態(tài)。
下圖為運(yùn)行效果:

認(rèn)識Join
join可能對于一些同學(xué)來說并不陌生,此處我就不詳細(xì)介紹Join是什么了,有疑問的同學(xué)可以自行baidu和google。
這里我將直接介紹如何使用join來達(dá)到我們希望看到的效果!

這里主要是利用Join的阻塞效果,來達(dá)到我們的使用目的??瓷蠄D的運(yùn)行結(jié)果可以得知,程序已經(jīng)按照我們指定的順序執(zhí)行結(jié)束了,并得到了我們想要的結(jié)果。
其實(shí)這里可以深入的思考一下,為什么join可以達(dá)到我們想要的效果呢?接下來我們來看下源碼:
進(jìn)入join源碼后,首先看到的是一個(gè)傳入0參數(shù)的join方法,此處選擇繼續(xù)進(jìn)入。

首先可以看到j(luò)oin方法是線程安全的,其次可以結(jié)合上圖一起看,當(dāng)傳入?yún)?shù)為0時(shí),會命中一個(gè)wait(0)的方法,有經(jīng)驗(yàn)的同學(xué)應(yīng)該能直接看懂,這里表示等待。
但是需要說明的是,這里的等待絕對不是等待調(diào)用者,而是阻塞的主線程,t1,t2,t3只是子線程,當(dāng)子線程運(yùn)行完畢后,主線程結(jié)束等待。
這里演示了join的工作方式,也證實(shí)了join能讓我們在程序中達(dá)到自己想要的效果。

除了join能在程序中幫助我們控制線程的順序外,還有另外的方式,比如我們利用線程池實(shí)現(xiàn)試一試。
利用Executors線程池
Executors是JDK中java.util.concurrent包下線程池操作類,可以方便的為我們提供線程池的操作。
這里我們使用Executors中的newSingleThreadExecutor()方法,創(chuàng)建一個(gè)單線程的線程池。

根據(jù)上圖可以得知,利用newSingleThreadExecutor()方法依然能夠達(dá)到我們期待的效果,其實(shí)原理很簡單,方法內(nèi)部是一個(gè)基于FIFO的隊(duì)列,也就是說,當(dāng)我們依次將t1,t2,t3加入隊(duì)列中時(shí),實(shí)際在就緒狀態(tài)的只有t1這個(gè)線程,t2,t3則會被添加到隊(duì)列中,當(dāng)t1執(zhí)行完畢后,則會繼續(xù)執(zhí)行隊(duì)列中的其他線程。
根據(jù)上面的篇幅我們得知了如何讓線程按照指定的方式運(yùn)行,其實(shí)方法還有很多,就不一一列舉了。
線程的優(yōu)先級及執(zhí)行順序
在學(xué)習(xí)運(yùn)算符時(shí),讀者知道各個(gè)運(yùn)算符之間有優(yōu)先級,了解運(yùn)算符的優(yōu)先級對程序幵發(fā)有很好的作用。線程也是如此,每個(gè)線程都具有優(yōu)先級,Java 虛擬機(jī)根據(jù)線程的優(yōu)先級決定線程的執(zhí)行順序,這樣使多線程合理共享 CPU 資源而不會產(chǎn)生沖突。
優(yōu)先級概述
在 Java 語言中,線程的優(yōu)先級范圍是 1~10,值必須在 1~10,否則會出現(xiàn)異常;優(yōu)先級的默認(rèn)值為 5。優(yōu)先級較高的線程會被優(yōu)先執(zhí)行,當(dāng)執(zhí)行完畢,才會輪到優(yōu)先級較低的線程執(zhí)行。如果優(yōu)先級相同,那么就采用輪流執(zhí)行的方式。
可以使用 Thread 類中的 setPriority() 方法來設(shè)置線程的優(yōu)先級。語法如下:
public final void setPriority(int newPriority);
如果要獲取當(dāng)前線程的優(yōu)先級,可以直接調(diào)用 getPriority() 方法。語法如下:
public final int getPriority();
使用優(yōu)先級
簡單了解過優(yōu)先級之后,下面通過一個(gè)簡單的例子來演示如何使用優(yōu)先級。
例 1
分別使用 Thread 類和 Runnable 接口創(chuàng)建線程,并為它們指定優(yōu)先級。
public class FirstThreadInput extends Thread
{
? ? public void run()
? ? {
? ? ? ? System.out.println("調(diào)用FirstThreadInput類的run()重寫方法"); ? ?//輸出字符串
? ? ? ? for(int i=0;i<5;i++)
? ? ? ? {
? ? ? ? ? ? System.out.println("FirstThreadInput線程中i="+i); ? ?//輸出信息
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.sleep((int) Math.random()*100); ? ?//線程休眠
? ? ? ? ? ? }
? ? ? ? ? ? catch(Exception e){}
? ? ? ? }
? ? }
}(2) 創(chuàng)建實(shí)現(xiàn) Runnable 接口的 SecondThreadInput 類,實(shí)現(xiàn) run() 方法。代碼如下:
public class SecondThreadInput implements Runnable
{
? ? public void run()
? ? {
? ? ? ? System.out.println("調(diào)用SecondThreadInput類的run()重寫方法"); ? ?//輸出字符串
? ? ? ? for(int i=0;i<5;i++)
? ? ? ? {
? ? ? ? ? ? System.out.println("SecondThreadInput線程中i="+i); ? ?//輸出信息
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.sleep((int) Math.random()*100); ? ?//線程休眠
? ? ? ? ? ? }
? ? ? ? ? ? catch(Exception e){}
? ? ? ? }
? ? }
}(3) 創(chuàng)建 TestThreadInput 測試類,分別使用 Thread 類的子類和 Runnable 接口的對象創(chuàng)建線程,然后調(diào)用 setPriority() 方法將這兩個(gè)線程的優(yōu)先級設(shè)置為 4,最后啟動線程。代碼如下:
public class TestThreadInput
{
? ? public static void main(String[] args)
? ? {
? ? ? ? FirstThreadInput fti=new FirstThreadInput();
? ? ? ? Thread sti=new Thread(new SecondThreadInput());
? ? ? ? fti.setPriority(4);
? ? ? ? sti.setPriority(4);
? ? ? ? fti.start();
? ? ? ? sti.start();
? ? }
}(4) 運(yùn)行上述代碼,運(yùn)行結(jié)果如下所示。
調(diào)用FirstThreadInput類的run()重寫方法
調(diào)用SecondThreadInput類的run()重寫方法
FirstThreadInput線程中i=0
SecondThreadInput線程中i=0
FirstThreadInput線程中i=1
FirstThreadInput線程中i=2
SecondThreadInput線程中i=1
FirstThreadInput線程中i=3
SecondThreadInput線程中i=2
FirstThreadInput線程中i=4
SecondThreadInput線程中i=3
SecondThreadInput線程中i=4
由于該例子將兩個(gè)線程的優(yōu)先級都設(shè)置為 4,因此它們交互占用 CPU ,宏觀上處于并行運(yùn)行狀態(tài)。
重新更改 ThreadInput 類的代碼、設(shè)置優(yōu)先級。代碼如下:
fti.setPriority(1); sti.setPriority(10);
重新運(yùn)行上述代碼,如下所示。
調(diào)用FirstThreadInput類的run()重寫方法
調(diào)用SecondThreadInput類的run()重寫方法
FirstThreadInput線程中i=0
SecondThreadInput線程中i=0
SecondThreadInput線程中i=1
SecondThreadInput線程中i=2
SecondThreadInput線程中i=3
SecondThreadInput線程中i=4
FirstThreadInput線程中i=1
FirstThreadInput線程中i=2
FirstThreadInput線程中i=3
FirstThreadInput線程中i=4
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
說說字符串轉(zhuǎn) OffSetDateTime 你真的會用嗎
這篇文章主要介紹了字符串轉(zhuǎn) OffSetDateTime 你真的會用嗎?具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java基于socket實(shí)現(xiàn)的客戶端和服務(wù)端通信功能完整實(shí)例
這篇文章主要介紹了Java基于socket實(shí)現(xiàn)的客戶端和服務(wù)端通信功能,結(jié)合完整實(shí)例形式分析了Java使用socket建立客戶端與服務(wù)器端連接與通信功能,需要的朋友可以參考下2018-05-05
Java分布式學(xué)習(xí)之Kafka消息隊(duì)列
Kafka是由Apache軟件基金會開發(fā)的一個(gè)開源流處理平臺,由Scala和Java編寫。Kafka是一種高吞吐量的分布式發(fā)布訂閱消息系統(tǒng),它可以處理消費(fèi)者在網(wǎng)站中的所有動作流數(shù)據(jù)2022-07-07
SpringBoot中Filter?bean如何添加到Servlet容器
這篇文章主要介紹了SpringBoot中Filter bean是怎么被添加到Servlet容器中的,本文分步驟結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
RxJava2.x+ReTrofit2.x多線程下載文件的示例代碼
本篇文章主要介紹了RxJava2.x+ReTrofit2.x多線程下載文件的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09
SiteMesh如何結(jié)合Freemarker及velocity使用
這篇文章主要介紹了SiteMesh如何結(jié)合Freemarker及velocity使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

