我們在日常的多執行緒開發中,有時可能會想讓每個執行緒都按照我們指定的順序來運行,而不是讓CPU隨機調度,這樣可能會讓我們在日常的開發工作中帶來不必要的麻煩。
既然有了這個需求,也就引入了本文的標題,讓執行緒按照自己指定的順序來運作。
有興趣的同學可以猜想下列程式碼可能運行的結果:
#依照正常的理解思路,上面程式碼的執行順序依序應為:t1 → t2 → t3,而實際效果則不是理想的狀態。
下圖為運行效果:
join可能對某些同學來說並不陌生,這裡我就不詳細介紹Join是什麼了,有疑問的同學可以自行baidu和google。
這裡我將直接介紹如何使用join來達到我們希望看到的效果!
這裡主要利用Join的阻塞效果,來達到我們的使用目的。看上圖的運行結果可以得知,程式已經按照我們指定的順序執行結束了,並且得到了我們想要的結果。
其實這裡可以深入的思考一下,為什麼join可以達到我們想要的效果呢?接下來我們來看下原始碼:
進入join原始碼後,首先看到的是一個傳入0參數的join方法,這裡選擇繼續進入。
首先可以看到join方法是執行緒安全的,其次可以結合上圖一起看,當傳入參數為0時,會命中一個wait(0)的方法,有經驗的同學應該能直接看懂,這裡表示等待。
但要說明的是,這裡的等待絕對不是等待呼叫者,而是阻塞的主線程,t1,t2,t3只是子線程,當子執行緒運行完畢後,主執行緒結束等待。
這裡示範了join的工作方式,也證實了join能讓我們在程式中達到自己想要的效果。
除了join能在程式中幫助我們控制執行緒的順序外,還有另外的方式,例如我們利用執行緒池實作試一試。
Executors是JDK中java.util.concurrent套件下線程池操作類,可以方便的為我們提供線程池的操作。
這裡我們使用Executors中的newSingleThreadExecutor()方法,建立一個單一執行緒的執行緒池。
根據上圖可以得知,利用newSingleThreadExecutor()方法仍然能夠達到我們期待的效果,其實原理很簡單,方法內部是一個基於FIFO的佇列,也是說,當我們依序將t1,t2,t3加入佇列時,實際在就緒狀態的只有t1這個線程,t2,t3則會被加入到佇列中,當t1執行完畢後,則會繼續執行佇列中的其他線程。
在學習運算子時,讀者知道各個運算子之間有優先權,了解運算子的優先權對程式幵發有很好的作用。執行緒也是如此,每個執行緒都具有優先權,Java 虛擬機器根據執行緒的優先權決定執行緒的執行順序,這使多執行緒合理共享 CPU 資源而不會產生衝突。
在 Java 語言中,執行緒的優先權範圍是 1~10,值必須在 1~10,否則會出現異常;優先權的預設值為 5。優先順序較高的執行緒會被優先執行,執行完畢,才會輪到優先權較低的執行緒執行。如果優先順序相同,那麼就採用輪流執行的方式。
可以使用 Thread 類別中的 setPriority() 方法來設定執行緒的優先權。語法如下:
public final void setPriority(int newPriority);
如果要取得目前執行緒的優先權,可以直接呼叫 getPriority() 方法。語法如下:
public final int getPriority();
簡單了解優先順序之後,以下透過一個簡單的範例來示範如何使用優先順序。
範例 1
分別使用 Thread 類別和 Runnable 介面建立線程,並為它們指定優先權。
public class FirstThreadInput extends Thread { public void run() { System.out.println("调用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) 建立實作 Runnable 介面的 SecondThreadInput 類,實作 run() 方法。程式碼如下:
public class SecondThreadInput implements Runnable { public void run() { System.out.println("调用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) 创建 TestThreadInput 测试类,分别使用 Thread 类的子类和 Runnable 接口的对象创建线程,然后调用 setPriority() 方法将这两个线程的优先级设置为 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) 运行上述代码,运行结果如下所示。
调用FirstThreadInput类的run()重写方法
调用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
由于该例子将两个线程的优先级都设置为 4,因此它们交互占用 CPU ,宏观上处于并行运行状态。
重新更改 ThreadInput 类的代码、设置优先级。代码如下:
fti.setPriority(1); sti.setPriority(10);
重新运行上述代码,如下所示。
调用FirstThreadInput类的run()重写方法
调用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
以上是Java中怎麼實作讓執行緒按照自己指定的順序執行的詳細內容。更多資訊請關注PHP中文網其他相關文章!