使用Java實現三個線程交替打印0-74
題目分析
三個線程交替打印,即3個線程是按順序執行的。一個線程執行完之后,喚醒下一個線程,然后阻塞,等待被該線程的上一個線程喚醒。執行的順序是一個環裝的隊列 0->1->2->0 ....->0->1->2
實現思路
由於三個線程一次只能有一個在打印數字,所以需要用一個鎖來進行同步。但是在打印時要保證順序就要求 一個線程打印完之后只能喚醒它的下一個線程,而不是喚醒所有的線程。這就要求給每一個線程都有一個自己的狀態來控制阻塞和喚醒。
java 並發包中的鎖(實現了Lock接口的ReentrantLock、ReentrantReadWriteLock)有一個newCondition方法。調用該方法會返回與該鎖綁定Condition對象實例。當線程獲取鎖之后,調用Condition實例的await方法會自動釋放線程的鎖,當其他線程調用該Condition對象實例的signal方法后,該線程會自動嘗試獲取鎖。
通過對Condition的分析可知,我們只要對三個線程生成三個Condition對象。當一個線程打印一個數字之后就調用下一個線程的Condition對象的signal方法喚醒下一個線程,然后調用自己的Condition的await線程進入等待狀態。這樣就實現了線程執行順序的控制。由於線程的執行是一個環形的隊列,我們用一個數組存放每個線程的Condition對象,通過對下標加一然后取模來實現環形隊列。
代碼
package com.test.concurrent.alternatingprint;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class PrintNumber extends Thread {
/**
* 多個線程共享這一個sequence數據
*/
private static int sequence=0;
private static final int SEQUENCE_END =75;
private Integer id;
private ReentrantLock lock;
private Condition[] conditions;
private PrintNumber(Integer id, ReentrantLock lock, Condition[] conditions) {
this.id = id;
this.setName("thread" + id);
this.lock = lock;
this.conditions = conditions;
}
@Override
public void run() {
while (sequence >= 0 && sequence < SEQUENCE_END) {
lock.lock();
try {
//對序號取模,如果不等於當前線程的id,則先喚醒其他線程,然后當前線程進入等待狀態
while (sequence % conditions.length != id) {
conditions[(id + 1) % conditions.length].signal();
conditions[id].await();
}
System.out.println(Thread.currentThread().getName() + " " + sequence);
//序號加1
sequence = sequence + 1;
//喚醒當前線程的下一個線程
conditions[(id + 1) % conditions.length].signal();
//當前線程進入等待狀態
conditions[id].await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//將釋放鎖的操作放到finally代碼塊中,保證鎖一定會釋放
lock.unlock();
}
}
//數字打印完畢,線程結束前喚醒其余的線程,讓其他線程也可以結束
end();
}
private void end() {
lock.lock();
conditions[(id + 1) % conditions.length].signal();
conditions[(id + 2) % conditions.length].signal();
lock.unlock();
}
public static void main(String[] args) {
int threadCount = 3;
ReentrantLock lock = new ReentrantLock();
Condition[] conditions = new Condition[threadCount];
for (int i = 0; i < threadCount; i++) {
conditions[i] = lock.newCondition();
}
PrintNumber[] printNumbers = new PrintNumber[threadCount];
for (int i = 0; i < threadCount; i++) {
PrintNumber p = new PrintNumber(i, lock, conditions);
printNumbers[i] = p;
}
for (PrintNumber printNumber : printNumbers) {
printNumber.start();
}
}
}