多线程环境下TreeMap.put()出现异常行为的原因咨询
问题:多线程下init()方法输出索引不唯一的原因分析
你运行了以下Java代码:
import java.util.Collections; import java.util.Map; import java.util.TreeMap; public final class ThreadTest { int N = 4; Map<Integer, Proxy> proxy = Collections.synchronizedMap(new TreeMap<>()); Proxy p; public static void main(String[] args) { new ThreadTest(); } public ThreadTest() { Thread[] t = new Thread[N]; for(int c=0;c<N;c++) { final int i = c; t[i] = new Thread(() -> { try { System.out.println( "From thread: " + i ); p = new Proxy(i); proxy.put(i, p); p.init(); } catch (Exception e){} }); t[i].start(); } for(int c=0;c<N;c++) { try { t[c].join(); } catch (InterruptedException e){} } } public class Proxy { int index; public Proxy(int index) { this.index = index; System.out.println( "Proxy: " + index ); } public void init() { System.out.println("Initializing " + index ); } } }
得到的示例输出如下:
From thread: 0 From thread: 1 From thread: 2 From thread: 3 Proxy: 0 Proxy: 3 Proxy: 2 Proxy: 1 Initializing 3 Initializing 3 Initializing 1 Initializing 2
尽管已使用Collections.synchronizedMap()对TreeMap进行同步,但init()方法输出的索引并非唯一,结果与未同步的Map完全一致,仅调用put()方法却出现索引异常的行为令人费解。
原因分析
- 问题核心不在
synchronizedMap的同步上,而是共享成员变量p的线程不安全访问:p是ThreadTest类的实例变量,所有线程共享这同一个变量。当多个线程交替执行p = new Proxy(i);时,会互相覆盖p的指向。比如线程0刚把p赋值为Proxy(0),还没来得及调用init(),线程3就将p替换为Proxy(3),此时线程0再执行p.init()时,实际调用的是线程3创建的Proxy实例的方法,因此会出现重复的"Initializing 3"。 Collections.synchronizedMap()仅保证proxy.put(i,p)操作本身是线程安全的,不会出现Map的结构损坏或数据丢失,但它无法管控你对共享变量p的非同步操作。- 解决验证:将
p改为线程内的局部变量,即把p = new Proxy(i);修改为Proxy p = new Proxy(i);,让每个线程持有独立的Proxy实例,就能避免索引重复的问题。
内容的提问来源于stack exchange,提问作者mike_uvw
相关产品推荐
相关产品推荐

