行動裝置資源受限(CPU、記憶體、網路頻寬),單一執行緒會導致 UI 凍結、網路阻塞、遊戲幀率下降。KVM (K Virtual Machine) 提供精簡但完整的 java.lang.Thread 與 java.util.Timer API,支援執行緒建立、優先級、睡眠、等待/通知等核心功能。
public class GameLoopThread extends Thread {
private volatile boolean running = true;
public void stopLoop() { running = false; }
@Override
public void run() {
while (running) {
// 遊戲邏輯更新
updateGameState();
// 重繪畫面
repaintCanvas();
// 幀率控制
try { Thread.sleep(40); } // ~25 FPS
catch (InterruptedException e) { break; }
}
}
private void updateGameState() { /* ... */ }
private void repaintCanvas() { /* 呼叫 Canvas.repaint() */ }
}
// 使用方式
GameLoopThread gameThread = new GameLoopThread();
gameThread.start(); // 自動呼叫 run()
// 遊戲結束時
gameThread.stopLoop();
public class NetworkTask implements Runnable {
private final String url;
public NetworkTask(String url) { this.url = url; }
@Override
public void run() {
HttpConnection conn = null;
try {
conn = (HttpConnection) Connector.open(url);
int rc = conn.getResponseCode();
if (rc == HttpConnection.HTTP_OK) {
InputStream is = conn.openInputStream();
// 解析回應...
parseResponse(is);
}
} catch (IOException e) {
handleError(e);
} finally {
closeQuietly(conn);
}
}
// 輔助方法略...
}
// 使用方式
Thread networkThread = new Thread(new NetworkTask("https://api.example.com/data"));
networkThread.start();
public class StatusReporter extends TimerTask {
private final DeviceClient client;
public StatusReporter(DeviceClient client) { this.client = client; }
@Override
public void run() {
// 定期上報裝置狀態
client.sendHeartbeat();
}
}
// 排程使用
Timer timer = new Timer();
// 3 秒後首次執行,之後每 30 秒執行一次
timer.schedule(new StatusReporter(client), 3000, 30000);
// 取消排程
timer.cancel(); // 也會取消所有已排程任務
timer.schedule(task, delay)。Timer 內部只有一個背景執行緒,**任務執行時間過長會阻塞後續任務**;耗時操作請在任務中另開執行緒。Timer 不支援固定頻率(fixed-rate)模式,只有固定延遲(fixed-delay)。| 方法 | 說明 |
|---|---|
start() | 將執行緒置為可執行狀態,由排程器決定何時執行 run(),**非立即執行** |
run() | 執行緒主體邏輯,**直接呼叫不會開新執行緒** |
sleep(ms) | 靜態方法,讓**目前執行緒**睡眠指定毫秒,不釋放鎖 |
yield() | 靜態方法,暗示排程器讓出 CPU 給同優先級執行緒 |
join() | 等待該執行緒終止 |
setPriority(int) | 設定優先級(MIN=1, NORM=5, MAX=10),KVM 排程器參考但不保證 |
isAlive() | 判斷執行緒是否仍在執行(已啟動且未終止) |
interrupt() | 中斷阻塞中的執行緒(sleep、wait、join 會拋出 InterruptedException) |
public class GameCanvas extends Canvas implements CommandListener {
private Command cmdSync = new Command("同步", Command.OK, 1);
private Command cmdExit = new Command("離開", Command.EXIT, 2);
public GameCanvas() {
addCommand(cmdSync);
addCommand(cmdExit);
setCommandListener(this);
}
public void commandAction(Command c, Displayable d) {
if (c == cmdSync) {
// ★ 關鍵:網路操作必須在新執行緒,避免阻塞 UI 執行緒導致裝置當機
new Thread(new Runnable() {
public void run() {
syncWithServer(); // 耗時 HTTP 請求
}
}).start();
} else if (c == cmdExit) {
notifyDestroyed();
}
}
private void syncWithServer() {
// 實作網路同步邏輯...
// 完成後可用 Display.callSerially() 回到 UI 執行緒更新畫面
}
}
Display.callSerially(Runnable) 將畫面更新排程回 UI 執行緒。volatile 控制迴圈旗標:volatile boolean running 確保跨執行緒可見性。InterruptedException 正確結束執行緒:不要吞掉中斷,重設中斷旗標或 break 迴圈。run() 直接操作 RecordStore:若頻繁存取,考慮單一寫入執行緒 + 佇列。總結:J2ME 雖輕量,但 Thread、Runnable、TimerTask 三大模式已足夠應對行動裝置的並發需求。核心原則:**UI 執行緒只做 UI,耗時工作全丟給背景執行緒**,並妥善處理生命週期與資源釋放。