You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何第二种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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 00:10:16