Java 平台自誕生之初即被設計為多線程環境。當主程式執行時,垃圾回收、事件處理等系統級任務已在背景悄然運作,這些本質上都是線程。多線程讓開發者能定義相互獨立、互不干擾的任務,系統透過 CPU 時間片輪換,讓這些任務看似同時執行。
Java 提供四大核心工具支援多線程開發:
java.util.Timer:適合簡單的週期性任務排程javax.swing.Timer:專為 Swing GUI 更新設計,執行於事件派發線程Thread 類別:提供底層線程控制能力Runnable 介面:支援任務與線程分離,突破單一繼承限制java.util.Timer 適合「每 500 毫秒執行一次」這類簡單需求。任務邏輯封裝於 TimerTask 的 run() 方法中:
import java.util.*;
public class Hi {
public static void main(String[] args) throws java.io.IOException {
TimerTask task = new TimerTask() {
public void run() {
System.out.println("Hi");
}
};
Timer timer = new Timer();
timer.schedule(task, 0, 500); // 立即啟動,每 500ms 執行一次
System.out.println("Press ENTER to stop");
System.in.read(new byte[10]);
timer.cancel();
}
}
關鍵點:JVM 只要有非守護線程存活就不會退出。timer.cancel() 終止排程線程後,程式才會結束。
javax.swing.Timer 與 java.util.Timer 運作類似,但兩大差異:
ActionListener 定義Swing 採單線程模型,所有元件操作必須在 EDT 執行。以下範例每 0.5 秒更新標籤計數:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Count {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
final JLabel label = new JLabel("", JLabel.CENTER);
label.setFont(new Font("Serif", Font.PLAIN, 36));
contentPane.add(label, BorderLayout.CENTER);
ActionListener listener = new ActionListener() {
int count = 0;
public void actionPerformed(ActionEvent e) {
count++;
label.setText(Integer.toString(count));
}
};
Timer timer = new Timer(500, listener);
timer.start();
frame.setSize(300, 100);
frame.setVisible(true);
}
}

當任務非單純重複、需長時間執行(如網路下載),Thread 類別提供完整控制權。兩種實作方式:
Thread 並覆寫 run()Runnable 介面,傳入 Thread 建構子因 Java 單一繼承限制,若類別已繼承其他類別,必須採用 Runnable 方式。
以下程式同時啟動兩個線程:thread1 下載 URL 內容並統計字元數,thread2 週期性列印訊息。主線程等待使用者輸入結束:
import java.io.*;
import java.net.*;
public class Both {
public static void main(String[] args) {
final String urlString = args[0];
final String message = args[1];
// 線程 1:下載並計算大小(匿名 Thread 子類別)
Thread thread1 = new Thread() {
public void run() {
try {
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
InputStreamReader isr = new InputStreamReader(connection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
int count = 0;
while (reader.read() != -1) count++;
System.out.println("Size is: " + count);
reader.close();
} catch (MalformedURLException e) {
System.err.println("Bad URL: " + urlString);
} catch (IOException e) {
System.err.println("I/O Problems");
}
}
};
thread1.start();
// 線程 2:週期性列印訊息(Runnable 實作)
Runnable runnable = new Runnable() {
public void run() {
while (true) {
System.out.println(message);
try { Thread.sleep(500); }
catch (InterruptedException e) {}
}
}
};
Thread thread2 = new Thread(runnable);
thread2.start();
// 主線程等待使用者輸入
try {
System.out.println("Press ENTER to stop");
System.in.read(new byte[10]);
} catch (IOException e) {
System.out.println("I/O problems");
}
System.exit(0);
}
}
掌握多線程是成為高效 Java 開發者的關鍵。選擇原則:
java.util.Timerjavax.swing.TimerThread + RunnableRunnable越早理解線程運作機制,越能洞悉 Java 程式的並行行為與互動模式。