Eclipse Rich Client Platform (RCP) 應用日益複雜,恰當運用多執行緒是提升回應速度與使用者體驗的關鍵。Eclipse 在 Java Thread 之上封裝了 Job、UIJob 及執行緒池機制,提供更高層級的並發控制 API。
| API | 適用場景 | 特色 |
|---|---|---|
Job | 長時間非 UI 運算、資料庫/網路存取 | 可排程、進度回報、規則控制並發、執行緒池重用 |
UIJob | 需在 UI 執行緒更新介面的短任務 | 自動在 UI 執行緒執行 runInUIThread |
Display.asyncExec() | 即時 UI 更新、事件發布 | 非阻塞、排入 UI 事件佇列尾端 |
Display.syncExec() | 需等待 UI 更新完成才繼續 | 阻塞呼叫執行緒直到 UI 執行完畢 |
schedule()。IProgressMonitor 整合進度對話框、取消按鈕。ISchedulingRule 宣告式限制同類 Job 並發數。JobManager 維護等待佇列與執行緒池。Job 狀態:
NONE:新建或已結束WAITING:已排程等待執行RUNNING:正在執行SLEEPING:延遲排程或 sleep() 中可用 job.getState() 查詢,job.wakeUp() 喚醒睡眠 Job。
SWTException。Display.getCurrent() != null。while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); }ISchedulingRule rule = new ISchedulingRule() {
public boolean contains(ISchedulingRule r) { return this.equals(r); }
public boolean isConflicting(ISchedulingRule r) { return this.equals(r); }
};
job1.setRule(rule);
job2.setRule(rule); // job2 會等 job1 結束才跑
UIJob refresh = new UIJob("更新") {
public IStatus runInUIThread(IProgressMonitor m) { text.setText("新"); return OK; }
public boolean shouldSchedule() { return !text.isDisposed(); }
public boolean shouldRun() { return !text.isDisposed(); }
};
refresh.schedule(10000); // 10 秒後執行,若 text 已銷毀則自動取消
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Job job = new Job("查詢資料") {
protected IStatus run(IProgressMonitor m) {
final List data = queryDatabase(); // 背景耗時
Display.getDefault().asyncExec(() -> {
tableViewer.setInput(data); // UI 執行緒更新
});
return Status.OK_STATUS;
}
};
job.schedule();
}
});
Object FAMILY = new Object();
viewer.addSelectionChangedListener(e -> {
Job.getJobManager().cancel(FAMILY); // 取消前一個待執行 Job
new UIJob("刷新") {
protected IStatus runInUIThread(IProgressMonitor m) { updateUI(); return OK; }
public boolean belongsTo(Object fam) { return fam == FAMILY; }
}.schedule(500); // 500ms 內無新事件才真正執行
});
try {
ModalContext.run(new IRunnableWithProgress() {
public void run(IProgressMonitor m) throws InvocationTargetException, InterruptedException {
fetchRemoteData(); // 非 UI 執行緒跑
ModalContext.checkCanceled(m);
}
}, true, new NullProgressMonitor(), Display.getCurrent());
} catch (Exception ex) { ... }
// 此處 UI 執行緒繼續,且期間介面仍可回應
Object FAMILY = new Object();
job1.setRule(rule); job1.belongsTo(FAMILY); // 實作 belongsTo 回傳 true
// ...
Job.getJobManager().cancel(FAMILY); // 全取消
Job.getJobManager().join(FAMILY, null); // 全等待
Job.getJobManager().sleep(FAMILY); // 全睡眠
Run → Suspend → 檢查呼叫堆疊。display.readAndDispatch() 或 display.sleep()。Thread.sleep():會佔住執行緒池執行緒,改用 job.sleep() 釋放執行緒回池。job.cancel() 僅設旗標,RUNNING 狀態 Job 需自行檢查 monitor.isCanceled() 並提早返回。等待取消完成:if (!job.cancel()) job.join();join():易造成死鎖,優先用 ModalContext 或事件驅動設計。widget.isDisposed(),避免使用者關閉視窗後 Job 仍嘗試更新導致例外。多執行緒能大幅提升 RCP 應用並發能力與使用者體驗,但也帶來複雜度、死鎖風險與效能開銷。原則:能不用就不用、用 Job 別用 Thread、用規則控制並發、用監視器回報進度、用 ModalContext 等待結果。實務上需針對具體場景權衡,本文模式供參考,實際專案請依需求調整。