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

Java 線程同步機制深度解析:synchronized、Lock、原子類實戰

     編輯:Java 相關知識

線程同步核心邏輯

多線程環境下,對共享資源的訪問必須遵循 加鎖 → 修改 → 釋放鎖 的原子性操作序列。Java 提供了多層次的同步機制,從關鍵字到顯式鎖再到無鎖算法,適用於不同並發場景。

1. synchronized 關鍵字:JVM 內置鎖

同步代碼塊

public class SyncBlockDemo {
    static class DataWrap { int counter = 0; }
    
    static class Worker extends Thread {
        private final DataWrap data;
        Worker(DataWrap d) { this.data = d; }
        
        @Override public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (data) { // 鎖住共享對象
                    data.counter++;
                    try { Thread.sleep(1); } catch (InterruptedException ignored) {}
                    System.out.println(getName() + " -> " + data.counter);
                }
            }
        }
    }
    
    public static void main(String[] args) {
        DataWrap shared = new DataWrap();
        new Worker(shared).start();
        new Worker(shared).start();
        new Worker(shared).start();
    }
}
  • 鎖對象必須是共享可見的引用
  • sleep()、yield() 不釋放鎖
  • 適用於簡單同步場景,代碼清晰

同步方法

public class SyncMethodDemo {
    static class DataWrap {
        int counter = 0;
        
        public synchronized void increment() { // 鎖住 this
            counter++;
            try { Thread.sleep(1); } catch (InterruptedException ignored) {}
            System.out.println(Thread.currentThread().getName() + " -> " + counter);
        }
    }
    
    static class Worker extends Thread {
        private final DataWrap data;
        Worker(DataWrap d) { this.data = d; }
        @Override public void run() {
            for (int i = 0; i < 10; i++) data.increment();
        }
    }
    
    public static void main(String[] args) {
        DataWrap shared = new DataWrap();
        new Worker(shared).start();
        new Worker(shared).start();
        new Worker(shared).start();
    }
}
  • 實例方法鎖 this,靜態方法鎖 Class 對象
  • 同一對象的所有同步方法互斥,即使調用不同方法
  • 避免在同步方法中調用耗時操作 (I/O、睡眠)

2. 顯式鎖 Lock 接口:更靈活的同步控制

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {
    static class DataWrap {
        private final Lock lock = new ReentrantLock(); // 可重入鎖
        int counter = 0;
        
        public void increment() {
            lock.lock(); // 手動加鎖
            try {
                counter++;
                try { Thread.sleep(1); } catch (InterruptedException ignored) {}
                System.out.println(Thread.currentThread().getName() + " -> " + counter);
            } finally {
                lock.unlock(); // 必須在 finally 中釋放
            }
        }
    }
    // Worker 與 main 類似...
}

Lock 核心優勢

讀寫鎖 ReadWriteLock:讀多寫少場景優化

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class CacheDemo {
    private final Map<String, Object> cache = new HashMap<>();
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    
    public Object get(String key) {
        rwLock.readLock().lock(); // 共享鎖,多線程可並發讀
        try { return cache.get(key); }
        finally { rwLock.readLock().unlock(); }
    }
    
    public void put(String key, Object value) {
        rwLock.writeLock().lock(); // 排他鎖,寫操作獨佔
        try { cache.put(key, value); }
        finally { rwLock.writeLock().unlock(); }
    }
}

3. 原子類:無鎖線程安全 (CAS)

import java.util.concurrent.atomic.*;

public class AtomicDemo {
    // 原子整數
    private final AtomicInteger counter = new AtomicInteger(0);
    // 原子引用
    private final AtomicReference<String> ref = new AtomicReference<>("init");
    // 原子數組
    private final AtomicIntegerArray array = new AtomicIntegerArray(10);
    // 字段更新器 (非侵入式)
    private static final AtomicIntegerFieldUpdater<Data> UPDATER = 
        AtomicIntegerFieldUpdater.newUpdater(Data.class, "value");
    
    static class Data { volatile int value = 0; }
    
    public void increment() {
        // 原子自增,返回新值
        int newVal = counter.incrementAndGet();
        // 比較並設置 (CAS)
        boolean updated = ref.compareAndSet("init", "updated");
        // 數組原子操作
        array.getAndIncrement(0);
        // 字段原子更新
        Data data = new Data();
        UPDATER.incrementAndGet(data);
    }
}

CAS 原理與 ABA 問題

  • CAS:Compare-And-Swap,硬件級原子指令,if (memory == expect) memory = update
  • 自旋:失敗後重試,適用於低競爭、短耗時場景
  • ABA 問題:值 A→B→A,CAS 認為未變。解決:AtomicStampedReference (版本戳) 或 AtomicMarkableReference (標記位)

4. 線程安全集合:避免手動同步

特性synchronizedLock
可中斷獲取鎖❌✅ lockInterruptibly()
嘗試非阻塞獲取❌✅ tryLock()
超時獲取鎖❌✅ tryLock(timeout, unit)
公平鎖選項❌ (非公平)✅ new ReentrantLock(true)
多條件變量❌ (單一 wait/notify)✅ newCondition()
死鎖檢測困難可通過 tryLock 實現避免
非線程安全同步包裝器並發專用 (推薦)
ArrayListCollections.synchronizedListCopyOnWriteArrayList
HashMapCollections.synchronizedMapConcurrentHashMap
HashSetCollections.synchronizedSetCopyOnWriteArraySet
Queue-ConcurrentLinkedQueue, ArrayBlockingQueue

ConcurrentHashMap 核心機制 (JDK 8+)

  • Node 數組 + 鏈表/紅黑樹
  • CAS + synchronized 細粒度鎖 (僅鎖頭節點)
  • 擴容支持多線程協助 transfer()
  • size() 使用 LongAdder 累加器

5. 高層同步工具類

// 計數器門閂:等待 N 個任務完成
CountDownLatch latch = new CountDownLatch(3);
executor.submit(() -> { doWork(); latch.countDown(); });
latch.await(); // 主線程阻塞直到歸零

// 循環柵欄:N 個線程相互等待
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全到齊"));
executor.submit(() -> { doWork(); barrier.await(); });

// 信號量:限制並發訪問數
Semaphore semaphore = new Semaphore(10); // 最多 10 個許可
semaphore.acquire();
try { doLimitedWork(); } finally { semaphore.release(); }

// 交換器:兩線程交換數據
Exchanger<Data> exchanger = new Exchanger<>();
executor.submit(() -> exchanger.exchange(produceData()));
executor.submit(() -> exchanger.exchange(consumeData()));

// Phaser: 可動態註冊的柵欄 (JDK 7+)
Phaser phaser = new Phaser(1); // 主線程註冊
for (int i = 0; i < 3; i++) {
    phaser.register();
    executor.submit(() -> { doPhase1(); phaser.arriveAndAwaitAdvance(); 
                            doPhase2(); phaser.arriveAndAwaitAdvance(); });
}
phaser.arriveAndDeregister();

6. 實戰選型指南

場景推薦方案理由
簡單計數器/標誌位AtomicInteger/AtomicBoolean無鎖、高性能、代碼簡潔
復雜業務邏輯互斥synchronized / ReentrantLock語義清晰、JVM 優化好、可重入
需要超時/中斷/公平性ReentrantLock顯式控制、診斷友好
讀多寫少緩存/配置ReadWriteLock / StampedLock讀並發、寫獨佔、StampedLock 更優
生產者消費者隊列BlockingQueue 實現類內置阻塞、無需手動 wait/notify
多階段任務協調Phaser / CyclicBarrier靈活的階段同步
高併發 Map/SetConcurrentHashMap / ConcurrentSkipListSet無鎖/細粒度鎖、擴展性強

7. 常見錯誤與避坑

  1. 鎖對象不共享:`new Object()` 每次不同、`String` 常量池、基本類型自動裝箱
  2. 釋放鎖漏寫:Lock 必須在 `finally` 中 `unlock()`
  3. 同步塊過大:包含 I/O、睡眠、複雜計算,降低並發度
  4. 死鎖:多鎖獲取順序不一致 → 統一全局獲取順序、使用 `tryLock` 超時退出
  5. 活鎖:過度禮讓重試 → 引入隨機退避
  6. volatile 誤用:只保證可見性,不保證複合操作原子性

掌握 Java 同步機制的層次與適用場景,是編寫高性能並發程序的基石。實際開發中優先使用高層並發工具 (JUC 包),減少底層鎖操作,並通過壓測驗證同步策略的有效性。

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