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

Java 並發編程:共享可變變量的線程安全四大解決方案

     編輯:Java 相關知識

線程不安全的根本原因

編寫線程安全代碼需關注兩個核心條件:

  1. 共享:多線程可同時訪問同一變量
  2. 可變:變量值在生命週期內會發生改變

只有同時滿足「共享」且「可變」時,才會產生線程安全問題。

經典反例:非線程安全計數器

public class UnsafeCounter {
    private int count = 0; // 共享且可變
    
    public void increase() { // 無同步機制
        count++; // 非原子操作:讀取-修改-寫入
    }
    
    public int getCount() {
        return count;
    }
}

count++ 編譯為三條位元組碼指令:getfield、iadd、putfield,多線程交執行時會導致更新丟失。

四大解決方案詳解

方案一:線程封閉 —— 不共享變量

將變量限制在單線程內部,天然線程安全。

// 方法區域變量:每次調用創建新副本,線程隔離
public int add(int count) {
    return ++count; // 區域變量在棧上,線程私有
}

// ThreadLocal:每線程獨立副本
private final ThreadLocal<Integer> threadLocalCount = 
    ThreadLocal.withInitial(() -> 0);

public void increase() {
    threadLocalCount.set(threadLocalCount.get() + 1);
}

適用場景:請求級數據、資料庫連線、SimpleDateFormat 等非線程安全物件。

方案二:不可變模式 —— 讓變量不可變

// final 修飾基本類型:引用不可變,值也不可變
private final int COUNT = 0;

// final 修飾引用類型:引用不可變,但對象內部狀態可能可變
private final Map<String, String> CONFIG = Collections.unmodifiableMap(new HashMap<>());

// 真正不可變對象:所有欄位 final、無 setter、不洩露 this 參考
public final class ImmutablePoint {
    private final int x, y;
    public ImmutablePoint(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }
}

核心原則:不可變對象永遠線程安全,無需同步。

方案三:同步機制 —— 協訪問順序

// 方法級同步:鎖定 this
public synchronized void increase() {
    count++;
}

// 代碼塊同步:縮小鎖範圍
public void increase() {
    synchronized(this) {
        count++;
    }
}

// 顯式 Lock:功能更豐富
private final ReentrantLock lock = new ReentrantLock();
public void increase() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}

方案四:原子變量類 —— 無鎖線程安全

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    private final AtomicInteger count = new AtomicInteger(0);
    
    public void increase() {
        count.incrementAndGet(); // 原子性自增
    }
    
    public int getCount() {
        return count.get();
    }
    
    // 複合操作仍需注意:compareAndSet 支援 CAS
    public boolean compareAndSet(int expect, int update) {
        return count.compareAndSet(expect, update);
    }
}

方案選擇決策樹

場景推薦方案理由
簡單計數/旗標AtomicInteger/AtomicBoolean無鎖、效能佳、API 簡潔
複雜不變式/多變量協同synchronized / ReentrantLock支援複雜邏輯、條件變量
請求級臨時數據線程封閉 / ThreadLocal零同步開銷、天然隔離
配置/常量/DTO不可變模式零同步、安全發布、易推理

常見誤區提醒

  • volatile 僅保證可見性,**不保證原子性**(如 i++ 仍不安全)
  • synchronized 方法鎖的是 this,靜態方法鎖的是 Class 對象
  • 集合類 Vector、Hashtable 雖同步但效能差,優先用並發集合
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved