承接第 1 部分對 Java 傳統多執行緒痛點的分析,本文正式進入 CSP (Communicating Sequential Processes) 理論與 JCSP (Java CSP) 函式庫的核心實作。重點涵蓋:
CSProcess、ChannelInput/Output、One2OneChannel 等run() 入口)。public interface CSProcess { public void run(); } (類似 Runnable 但語義更強)。One2OneChannel:1 寫 1 讀One2AnyChannel:1 寫多讀 (讀者競爭)Any2OneChannel:多寫 1 讀 (寫者競爭)Any2AnyChannel:多寫多讀ChannelInput/Output) 與基本型別通道 (ChannelInputInt/OutputInt 等),避免裝箱開銷。Parallel 接受 CSProcess[],內部進程可再是 Parallel。// 寫入端
synchronized void write(Object obj) {
buffer.put(obj); // 狀態 EMPTY → FULL
wait(); // 釋放鎖,阻塞等待讀取
}
// 讀取端
synchronized Object read() {
while (buffer.isEmpty()) wait();
Object obj = buffer.get(); // 狀態 FULL → EMPTY
notify(); // 喚醒寫入端
return obj;
}
利用 Java 監視器 (synchronized + wait/notify) 實現 Rendezvous 同步,JCSP 將這些底層細節封裝於 Channel 內部。
Barrier (N 方柵欄)。run() 返回。public class SendEvenIntsProcess implements CSProcess {
private ChannelOutputInt out;
public SendEvenIntsProcess(ChannelOutputInt out) { this.out = out; }
public void run() {
for (int i = 2; i <= 100; i += 2)
out.write(i);
}
}
public class ReadEvenIntsProcess implements CSProcess {
private ChannelInputInt in;
public ReadEvenIntsProcess(ChannelInputInt in) { this.in = in; }
public void run() {
while (true) {
int val = in.read();
System.out.println("Read: " + val);
}
}
}
public class DriverProgram {
public static void main(String[] args) {
One2OneChannelInt chan = new One2OneChannelInt();
new Parallel(new CSProcess[] {
new SendEvenIntsProcess(chan.out()),
new ReadEvenIntsProcess(chan.in())
}).run();
}
}
PlusInt:兩輸入相加 → 輸出Delta2Int:單輸入廣播 → 兩輸出PrefixInt:預置初始值 (0) → 後續透傳輸入public class IntegrateInt implements CSProcess {
private ChannelInputInt in;
private ChannelOutputInt out;
public IntegrateInt(ChannelInputInt in, ChannelOutputInt out) {
this.in = in; this.out = out;
}
public void run() {
One2OneChannelInt a = new One2OneChannelInt();
One2OneChannelInt b = new One2OneChannelInt();
One2OneChannelInt c = new One2OneChannelInt();
new Parallel(new CSProcess[] {
new PlusInt(in, c, a), // in + c → a
new Delta2Int(a, out, b), // a → out, b
new PrefixInt(0, b, c) // 0 → c, then b → c
}).run();
}
}
Channel a: [0, 1, 2, 3, 4, 5, ...]
Channel b: [0, 1, 3, 6, 10, 15, ...] // 三角數
Channel out: [1, 4, 9, 16, 25, 36, ...] // 平方數!
IntegrateInt 輸出為輸入整數的中間累加和,配合 PrefixInt 初始 0,實現流式累加。
public class SquaresInt implements CSProcess {
private ChannelOutputInt out;
public SquaresInt(ChannelOutputInt out) { this.out = out; }
public void run() {
One2OneChannelInt a = new One2OneChannelInt();
One2OneChannelInt b = new One2OneChannelInt();
new Parallel(new CSProcess[] {
new NumbersInt(a), // 0,1,2,3... → a
new IntegrateInt(a, b), // a 累加 → b (三角數)
new PairsInt(b, out) // b 相鄰兩數相加 → out (平方數)
}).run();
}
}
第 n 步驟:NumbersInt 輸出 n → IntegrateInt 累加為 n(n-1)/2 → PairsInt 計算 (n-1)(n-2)/2 + n(n-1)/2 = (n-1)²。完美生成平方數序列。
關鍵洞見:SquaresInt 將 IntegrateInt 視為黑盒組件,體現 CSP遞歸組合性。
輸出取決於「事件發生順序」而非單純輸入值 (如多路複用、超時控制、優先級搶佔)。
| 方法 | 策略 | 行為 |
|---|---|---|
select() | 任意選擇 | 就緒 Guard 中隨機挑一個 |
priSelect() | 優先級選擇 | 陣列索引最小 (優先級最高) 的就緒 Guard |
fairSelect() | 公平選擇 | 輪詢就緒 Guard,避免飢餓 |
AltingChannelInput/Int):通道有資料可讀時就緒。CSTimer):達到絕對時間點就緒。Skip):永遠就緒 (用於非阻塞輪詢)。public class ScaleInt implements CSProcess {
private int factor = 2;
// 正常模式 Guards: suspend(最高), timer, in
// 暫停模式 Guards: inject(最高), in
public void run() {
Alternative normalAlt = new Alternative(new Guard[]{suspend, timer, in});
Alternative suspendedAlt = new Alternative(new Guard[]{inject, in});
long timeout = timer.read() + 5000;
timer.setAlarm(timeout);
while (true) {
switch (normalAlt.priSelect()) {
case 0: // suspend
suspend.read(); factor.write(factor);
boolean suspended = true;
while (suspended) {
switch (suspendedAlt.priSelect()) {
case 0: factor = inject.read(); suspended = false; timeout = timer.read()+5000; timer.setAlarm(timeout); break;
case 1: out.write(in.read()); break; // 透傳
}
}
break;
case 1: // timer
timeout = timer.read()+5000; timer.setAlarm(timeout); factor *= 2; break;
case 2: // in
out.write(factor * in.read()); break;
}
}
}
}
public class Controller implements CSProcess {
public void run() {
CSTimer tim = new CSTimer();
while (true) {
tim.after(6000); // 等待 6 秒
suspend.write(0); // 發送暫停
int curr = factor.read(); // 讀取因子
curr++; // 簡單計算新因子
inject.write(curr); // 注入並喚醒
}
}
}
new Parallel(new CSProcess[] {
new NumbersInt(temp), // 自然數源
new FixedDelayInt(1000, temp, in), // 1 秒延遲
new ScaleInt(2, suspend, in, factor, inject, out),
new Controller(6000, suspend, inject, factor),
new PrinterInt(out, "--> ", "\n")
}).run();
CSProcess、Channel、Parallel 三大核心構造。IntegrateInt → SquaresInt → 更大系統。select()/wait() 迴圈。下一部分將探討 CSP 與 AOP 的結構相似性、Barrier/Bucket/CREW 等高級同步構造、ProcessManager 動態管理、JCSP.Net 分布式擴展,以及與
java.util.concurrent的深度比較。
Abhijit Belapurkar,IIT Delhi 計算機科學學士,Infosys 班加羅爾高級技術架構師。