为何第二种synchronized同步块实现无法得到正确结果?
为什么第二种加了
synchronized的List线程安全实现会出错? 众所周知ArrayList这类List实现类并非线程安全,下面两种实现都尝试用synchronized关键字解决线程安全问题,但结果却不一样:
实现1(结果始终正确)
public class TestSyn { public static void main( String[] args ) throws InterruptedException { List<String> list = new ArrayList<>(); for( int i = 0; i < 100000; i++ ) { new Thread( ()-> { synchronized( list ) { list.add( Thread.currentThread().getName() ); } } ).start(); } Thread.sleep( 2000 ); System.out.println( list.size() ); } }
实现2(结果有时小于预期值)
public class TestSyn2 { public static void main( String[] args ) throws InterruptedException { List<String> list = new ArrayList<>(); synchronized( list ) { for( int i = 0; i < 10000; i++ ) { new Thread( () -> list.add( Thread.currentThread().getName() ) ) .start(); } } Thread.sleep( 2000 ); System.out.println( list.size() ); } }
问题分析
两种实现的核心差异在于**synchronized锁的作用范围完全不同**:
实现1中,每个新线程在执行
list.add()这个线程不安全的操作时,都会先获取list对象的锁,确保同一时刻只有一个线程能执行add操作,所有对list的修改都是原子性的,因此最终size始终等于线程创建的数量。实现2中,
synchronized锁是在main线程的循环外层,它只保证了main线程创建10000个线程的过程是同步的——但这完全没作用!真正修改list的是那些新创建的线程,而这些线程的run方法里没有任何同步逻辑。main线程的同步块执行完毕后就会释放锁,此时所有新线程会同时并发执行list.add(),没有锁保护的情况下必然出现线程安全问题(比如多个线程同时扩容导致元素丢失、计数更新时的竞态条件),最终size就会小于预期值。
内容的提问来源于stack exchange,提问作者liuzx
相关产品推荐
相关产品推荐

