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

Java 多線程編程:共享對象與數據訪問的兩種核心模式

     編輯:Java 相關知識

兩種基本場景

多線程訪問共享數據時,代碼結構通常分為兩類:

  1. 代碼相同,數據共享:如賣票系統,多窗口執行同一邏輯,操作同一票池。
  2. 代碼不同,數據共享:如生產者-消費者,增量線程與減量線程邏輯不同,共享計數器。

模式一:同一 Runnable 實例共享數據(代碼相同)

public class TicketSeller implements Runnable {
    private int tickets = 100; // 共享數據

    @Override
    public void run() {
        while (true) {
            synchronized (this) { // 鎖住當前實例
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + " 賣出第 " + tickets-- + " 張票");
                } else {
                    break;
                }
            }
        }
    }
}

// 使用:同一實例傳給多線程
TicketSeller seller = new TicketSeller();
new Thread(seller, "窗口A").start();
new Thread(seller, "窗口B").start();
new Thread(seller, "窗口C").start();

關鍵點:單一 Runnable 實例封裝共享狀態,synchronized(this) 保證原子性。

模式二:不同 Runnable 實例共享數據(代碼不同)

方案 A:獨立共享對象 + 操作方法封裝(推薦)

// 共享數據封裝類
class Counter {
    private int count = 0;

    public synchronized void increment() { count++; }
    public synchronized void decrement() { count--; }
    public synchronized int get() { return count; }
}

// 增量任務
class IncrementTask implements Runnable {
    private final Counter counter;
    public IncrementTask(Counter counter) { this.counter = counter; }
    @Override public void run() { for (int i=0;i<100;i++) counter.increment(); }
}

// 減量任務
class DecrementTask implements Runnable {
    private final Counter counter;
    public DecrementTask(Counter counter) { this.counter = counter; }
    @Override public void run() { for (int i=0;i<100;i++) counter.decrement(); }
}

// 使用
Counter shared = new Counter();
new Thread(new IncrementTask(shared)).start();
new Thread(new DecrementTask(shared)).start();

方案 B:內部類訪問外部類字段

public class SharedDataDemo {
    private int sharedCount = 0; // 外部類字段

    // 內部類自動持有外部類引用
    class Increment implements Runnable {
        @Override public void run() {
            for (int i=0;i<100;i++) {
                synchronized (SharedDataDemo.this) { sharedCount++; }
            }
        }
    }
    class Decrement implements Runnable {
        @Override public void run() {
            for (int i=0;i<100;i++) {
                synchronized (SharedDataDemo.this) { sharedCount--; }
            }
        }
    }

    public void start() {
        new Thread(new Increment()).start();
        new Thread(new Decrement()).start();
    }
}

核心設計原則

  • 封裝性:將共享數據與操作方法綁定在同一類中(如 Counter),由類內部負責同步,調用者無感知。
  • 最小化鎖範圍:同步塊僅覆蓋必要操作,避免在鎖內調用耗時/阻塞方法。
  • 不可變對象優先:若共享數據不可變(如 final String、Enum、Guava ImmutableList),無需同步。
  • 線程安全集合:優先使用 ConcurrentHashMap、CopyOnWriteArrayList、AtomicInteger 等無鎖/細粒度鎖組件,減少手動同步。

常見錯誤與避坑

錯誤模式後果修正
不同 Runnable 實例,各自擁有獨立數據副本數據不共享,線程操作各自副本構造時注入同一共享對象引用
用非 final 變量在匿名內部類中編譯錯誤或捕獲變量副本用 final 或 efectively final 變量;或封裝為可變容器
synchronized 鎖對象不一致同步失效確保所有線程鎖住同一對象(共享對象實例或 class 字面量)
雙重檢查鎖定未加 volatile指令重排導致部分初始化對象可見共享引用加 volatile
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved