首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 多線程編程精要 (一):Thread 與 Runnable 實現線程的兩種方式及同步基礎

     編輯:Java 程式設計入門

線程基礎認知

Java 內建多線程支援,`main()` 方法本身即運行於主線程。創建新線程有兩條標準路徑:繼承 `Thread` 或實現 `Runnable` (Java 8+ 推薦 Lambda/方法引用)。

方式一:繼承 Thread 類

class TimePrinter extends Thread {
    private final int intervalMs;
    private final String name;

    public TimePrinter(int intervalMs, String name) {
        this.intervalMs = intervalMs;
        this.name = name;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                System.out.printf("%s: %s%n", name, new java.util.Date());
                Thread.sleep(intervalMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 保持中斷狀態
                break;
            }
        }
    }
}

// 使用
TimePrinter fast = new TimePrinter(1000, "Fast");
TimePrinter slow = new TimePrinter(3000, "Slow");
fast.start(); // 必須呼叫 start(),而非 run()
slow.start();

方式二:實現 Runnable 介面 (推薦)

class TimePrinter implements Runnable {
    private final int intervalMs;
    private final String name;

    public TimePrinter(int intervalMs, String name) {
        this.intervalMs = intervalMs;
        this.name = name;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                System.out.printf("%s: %s%n", name, new java.util.Date());
                Thread.sleep(intervalMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

// 使用:將 Runnable 包裝進 Thread
Thread t1 = new Thread(new TimePrinter(1000, "Fast"));
Thread t2 = new Thread(new TimePrinter(3000, "Slow"));
t1.start();
t2.start();

// Java 8+ Lambda 更簡潔
Thread t3 = new Thread(() -> {
    while (!Thread.interrupted()) {
        System.out.println("Lambda: " + new java.util.Date());
        try { Thread.sleep(2000); } catch (InterruptedException e) { break; }
    }
});
t3.start();

兩種方式比較

維度extends Threadimplements Runnable
類別層次佔用單繼承名額不影響類別繼承體系
職責分離線程控制與任務邏輯混合任務邏輯獨立,可重用、可提交給執行緒池
資源共享需額外傳遞共享物件天然共享同一 Runnable 實例的欄位
現代最佳實踐極少使用標準寫法,配合 ExecutorService

關鍵字 `synchronized`:同步基礎

問題場景:銀行帳戶並發存取

class Account {
    private String owner;
    private double balance;

    public Account(String owner, double balance) {
        this.owner = owner;
        this.balance = balance;
    }

    // 非線程安全
    public void deposit(double amt) { balance += amt; }
    public void withdraw(double amt) { balance -= amt; }
    public double getBalance() { return balance; }
}

方案一:同步實例方法

public synchronized void deposit(double amt) { balance += amt; }
public synchronized void withdraw(double amt) { balance -= amt; }

方案二:同步塊 (細粒度鎖)

public void deposit(double amt) {
    synchronized (this) { balance += amt; }
}

方案三:私有鎖物件 (封裝鎖)

private final Object balanceLock = new Object();
public void deposit(double amt) {
    synchronized (balanceLock) { balance += amt; }
}

同步核心規則

  1. 鎖對象一致性:保護同一共享資源的所有讀寫操作,必須鎖定**同一個對象** (通常是 `this`、私有 `final Object` 或 `Class` 物件)。
  2. 最小化同步範圍:只同步必要代碼,避免在同步塊中呼叫 I/O、睡眠、外部方法。
  3. 避免死鎖:多鎖時**固定獲取順序** (如按 `hashCode` 排序);嘗試 `tryLock` (需 `Lock` 介面)。
  4. volatile 不替代 synchronized:`volatile` 僅保證可見性,不保證原子性 (如 `i++` 非原子)。

現代替代方案 (Java 5+)

  • Atomic 類:`AtomicInteger`、`AtomicReference` — 無鎖 CAS 操作。
  • Lock 介面:`ReentrantLock`、`ReadWriteLock` — 可中斷、可超時、公平鎖、條件變數。
  • 並發集合:`ConcurrentHashMap`、`CopyOnWriteArrayList`、`BlockingQueue` — 免同步高效能。
  • ExecutorService:`Executors.newFixedThreadPool()` — 統一線程生命週期管理,避免手動 `new Thread()`。

下一章預告

《Java 多線程編程精要 (二)》將深入講解:wait/notify 機制、生產者消費者模式、ThreadLocal、CompletableFuture 非阻塞組合、虛擬線程。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved