如何確保三個執行緒順序執行?
場景:有三個執行緒t1、t2、t3。確保三個執行緒t1執行完後t2執行,t2執行完成後t3執行。
1.使用join
thread.Join把指定的執行緒加入到當前執行緒,可以將兩個交替執行的執行緒合併為順序執行的執行緒。比如線上程B中呼叫了執行緒A的Join()方法,直到執行緒A執行完畢後,才會繼續執行執行緒B。
t.join(); //呼叫join方法,等待執行緒t執行完畢
t.join(1000); //等待 t 執行緒,等待時間是1000毫秒。
public class ThreadTest1 {
// T1、T2、T3三個執行緒順序執行
public static void main(String[] args ) {
Thread t1 = new Thread(new Work(null));
Thread t2 = new Thread(new Work(t1));
Thread t3 = new Thread(new Work(t2));
t1.start();
t2.start();
t3.start();
}
static class Work implements Runnable {
private Thread beforeThread;
public Work(Thread beforeThread) {
this .beforeThread = beforeThread;
}
public void run() {
if (beforeThread != null) {
try {
beforeThread.join();
System.out.println("thread start:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("thread start:" + Thread.currentThread().getName());
}
}
}
}
2.使用CountDownLatch
CountDownLatch(閉鎖)是一個很有用的工具類,利用它我們可以攔截一個或多個執行緒使其在某個條件成熟後再執行。它的內部提供了一個計數器,在構造閉鎖時必須指定計數器的初始值,且計數器的初始值必須大於0。另外它還提供了一個countDown方法來操作計數器的值,每呼叫一次countDown方法計數器都會減1,直到計數器的值減為0時就代表條件已成熟,所有因呼叫await方法而阻塞的執行緒都會被喚醒。這就是CountDownLatch的內部機制,看起來很簡單,無非就是阻塞一部分執行緒讓其在達到某個條件之後再執行。
public class ThreadTest2 {
// T1、T2、T3三個執行緒順序執行
public static void main(String[] args) {
CountDownLatch c0 = new CountDownLatch(0); //計數器為0
CountDownLatch c1 = new CountDownLatch(1); //計數器為1
CountDownLatch c2 = new CountDownLatch(1); //計數器為1
Thread t1 = new Thread(new Work(c0, c1));
//c0為0,t1可以執行。t1的計數器減1
Thread t2 = new Thread(new Work(c1, c2));
//t1的計數器為0時,t2才能執行。t2的計數器c2減1
Thread t3 = new Thread(new Work(c2, c2));
//t2的計數器c2為0時,t3才能執行
t1.start();
t2.start();
t3.start();
}
//定義Work執行緒類,需要傳入開始和結束的CountDownLatch引數
static class Work implements Runnable {
CountDownLatch c1;
CountDownLatch c2;
Work(CountDownLatch c1, CountDownLatch c2) {
super();
this.c1 = c1;
this.c2 = c2;
}
public void run() {
try {
c1.await();//前一執行緒為0才可以執行
System.out.println("thread start:" + Thread.currentThread().getName());
c2.countDown();//本執行緒計數器減少
} catch (InterruptedException e) {
}
}
}
}
3.CachedThreadPool
FutureTask一個可取消的非同步計算,FutureTask 實現了Future的基本方法,提空 start cancel 操作,可以查詢計算是否已經完成,並且可以獲取計算的結果。結果只可以在計算完成之後獲取,get方法會阻塞當計算沒有完成的時候,一旦計算已經完成,那麼計算就不能再次啟動或是取消。
一個FutureTask 可以用來包裝一個 Callable 或是一個runnable物件。因為FurtureTask實現了Runnable方法,所以一個 FutureTask可以提交(submit)給一個Excutor執行(excution).
public class ThreadTest3 {
// T1、T2、T3三個執行緒順序執行
public static void main(String[] args) {
FutureTask<Integer> future1= new FutureTask<Integer>(new Work(null));
Thread t1 = new Thread(future1);
FutureTask<Integer> future2= new FutureTask<Integer>(new Work(future1));
Thread t2 = new Thread(future2);
FutureTask<Integer> future3= new FutureTask<Integer>(new Work(future2));
Thread t3 = new Thread(future3);
t1.start();
t2.start();
t3.start();
}
static class Work implements Callable<Integer> {
private FutureTask<Integer> beforeFutureTask;
public Work(FutureTask<Integer> beforeFutureTask) {
this.beforeFutureTask = beforeFutureTask;
}
public Integer call() throws Exception {
if (beforeFutureTask != null) {
Integer result = beforeFutureTask.get();//阻塞等待
System.out.println("thread start:" + Thread.currentThread().getName());
} else {
System.out.println("thread start:" + Thread.currentThread().getName());
}
return 0;
}
}
}
4.使用blockingQueue
阻塞佇列 (BlockingQueue)是Java util.concurrent包下重要的資料結構,BlockingQueue提供了執行緒安全的佇列訪問方式:當阻塞佇列進行插入資料時,如果佇列已滿,執行緒將會阻塞等待直到佇列非滿;從阻塞佇列取資料時,如果佇列已空,執行緒將會阻塞等待直到佇列非空。併發包下很多高階同步類的實現都是基於BlockingQueue實現的。
public class ThreadTest4 {
// T1、T2、T3三個執行緒順序執行
public static void main(String[] args) {
//blockingQueue保證順序
BlockingQueue<Thread> blockingQueue = new LinkedBlockingQueue<Thread>();
Thread t1 = new Thread(new Work());
Thread t2 = new Thread(new Work());
Thread t3 = new Thread(new Work());
blockingQueue.add(t1);
blockingQueue.add(t2);
blockingQueue.add(t3);
for (int i=0;i<3;i++) {
Thread t = null;
try {
t = blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
t.start();
//檢測執行緒是否還活著
while (t.isAlive());
}
}
static class Work implements Runnable {
public void run() {
System.out.println("thread start:" + Thread.currentThread().getName());
}
}
}
5.使用單個執行緒池
newSingleThreadExecutor返回以個包含單執行緒的Executor,將多個任務交給此Exector時,這個執行緒處理完一個任務後接著處理下一個任務,若該執行緒出現異常,將會有一個新的執行緒來替代。
public class ThreadTest5 {
public static void main(String[] args) throws InterruptedException {
final Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " run 1");
}
}, "T1");
final Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " run 2");
try {
t1.join(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "T2");
final Thread t3 = new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " run 3");
try {
t2.join(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "T3");
//使用 單個任務的執行緒池來實現。保證執行緒的依次執行
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(t1);
executor.submit(t2);
executor.submit(t3);
executor.shutdown();
}
}