常看到在易變域上的同步代碼,而且寫的同窗會很天然以爲這樣是安全和正確的。
# 問題分析見文章連接:在易變域上的同步,對應的英文文章:Synchronization on mutable fields
Demo類com.oldratlee.fucking.concurrency.SynchronizationOnMutableFieldDemo
。java
主線程中開啓2個任務線程執行addListener
。主線程最終結果檢查。git
最終Listener
的個數不對。github
mvn compile exec:java -Dexec.mainClass=com.oldratlee.fucking.concurrency.SynchronizationOnMutableFieldDemo
public class SynchronizationOnMutableFieldDemo { static final int ADD_COUNT = 10000; static class Listener { // stub class } private volatile List<Listener> listeners = new CopyOnWriteArrayList<Listener>(); public static void main(String[] args) throws Exception { SynchronizationOnMutableFieldDemo demo = new SynchronizationOnMutableFieldDemo(); Thread thread1 = new Thread(demo.getConcurrencyCheckTask()); thread1.start(); Thread thread2 = new Thread(demo.getConcurrencyCheckTask()); thread2.start(); thread1.join(); thread2.join(); int actualSize = demo.listeners.size(); int expectedSize = ADD_COUNT * 2; if (actualSize != expectedSize) { // 在個人開發機上,幾乎必現!(簡單安全的解法:final List字段並用併發安全的List,如CopyOnWriteArrayList) System.err.printf("Fuck! Lost update on mutable field! actual %s expected %s.\n", actualSize, expectedSize); } else { System.out.println("Emm... Got right answer!!"); } } public void addListener(Listener listener) { synchronized (listeners) { List<Listener> results = new ArrayList<Listener>(listeners); results.add(listener); listeners = results; } } ConcurrencyCheckTask getConcurrencyCheckTask() { return new ConcurrencyCheckTask(); } private class ConcurrencyCheckTask implements Runnable { @Override public void run() { System.out.println("ConcurrencyCheckTask started!"); for (int i = 0; i < ADD_COUNT; ++i) { addListener(new Listener()); } System.out.println("ConcurrencyCheckTask stopped!"); } } }