为什么同步块会失效?

问题描述 投票:0回答:1

众所周知List不是线程安全的,我在下面所示的2个方法的代码中添加了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() );
  }
}
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() );
  }
}

第一种方法的结果总是10000,这是正确的,而第二种方法有时会小于10000。

那么,为什么第二个不能输出正确的结果呢?

java arraylist thread-safety synchronized synchronized-block
1个回答
0
投票

只是扩展评论中已经提供的答案(上面)。


两个版本之间的区别在于哪个或多个线程锁定

list
对象的内在互斥体。

在第一个版本中,

synchronized(list)
语句由程序创建的 100,000 个新线程中的每一个执行。它按词法包含在为每个新线程生成
Runnable
委托的 lambda 表达式中。

在第二个版本中,

synchronized(list)
语句仅由程序的
main
线程执行一次。它出现在 lambda 之外。这 100000 个新线程在没有任何同步的情况下改变了共享列表。事实上
main()
synchronized
块内执行这一事实没有任何意义,因为没有其他线程尝试在同一个对象上同步。


仅供参考:虽然你的程序的第一个版本是“安全的”,但它也是毫无意义的。您的 100000 个新线程中没有一个在

synchronized
块之外执行任何操作。这意味着 100000 个新线程中没有一个与其他线程同时运行。但并发性是创建线程的唯一原因。

你可以这样做,从各方面来说都会更好:

public class TestSyn 
{ 
  public static void main( String[] args ) throws InterruptedException 
  {
    List<String> list = new ArrayList<>();
    for( int i = 0; i < 100000; i++ ) 
    {
        list.add( Thread.currentThread().getName() );
    }
    Thread.sleep( 2000 );
    System.out.println( list.size() );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.