常看到在易变域上的同步代码,而且写的同窗会很天然以为这样是安全和正确的。
# 问题分析见文章连接:在易变域上的同步,对应的英文文章: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!"); } } }