众所周知,该列表不是线程安全的,我在下面所示的两个方法中向代码添加了同步.

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.

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

推荐答案

只是在上面的 comments 中已经提供了答案的基础上进行了扩展.


两个版本之间的区别在于哪个或哪些线程锁定了list对象的内部互斥锁.

在第一个版本中,synchronized(list)语句由程序创建的synchronized(list),000个新线程中的每一个执行.它包含在 for each 新线程生成Runnable个委托的lambda表达式中.

在第二个版本中,synchronized(list)语句只被程序的main线程执行一次.它出现在Lambda的外面.synchronized(list),000个新线程在没有任何同步的情况下改变了共享列表,并且main()synchronized块内执行的事实没有任何意义,因为从来没有其他线程try 在同一对象上同步.


Also note:虽然你的程序的第一个版本是"安全的",但它也是毫无意义的.您的synchronized,000个新线程中没有一个线程会在synchronized块之外执行任何操作.这意味着,您的synchronized,000个新线程中没有一个线程会与其他任何线程并发执行任何操作.但是并发是创建线程的only个原因.

Java相关问答推荐

为什么JFrame paint()多次绘制同一点(或根本不绘制)?

Maven Google Sheets版本问题

将Nimbus设置为计算机上运行的所有Java应用程序的默认外观

在Java中,如何按一个属性升序,然后按另一个属性降序对对象列表进行排序?

由于我在Main方法中关闭了 scanner ,但在该方法中创建了一个新的 scanner ,因此出现了错误

Com.example.service.QuestionService中的构造函数的参数0需要找不到的类型为';com.example.Dao.QuestionDao;的Bean

什么是Java原子属性的正确getter和setter

如何只修改父类ChroniclerView位置0处的第一个嵌套ChroniclerView(child)元素?

如何从日志(log)行中删除包名称?

在学习Spring时,通过构造函数参数0表达了不满意的依赖关系

与IntArray相比,ArrayList<;Int>;对于大量元素的性能极差

使用SWIG将C++自定义单元类型转换为基本Java类型

为什么我不能建立输入/输出流?Java ServerSocket

如何使用jooq更新记录?

无法在IntStream上应用Collectors.groupingBy

如何在右击时 Select 新行?

根据应用程序 Select 的语言检索数据

设置背景时缺少Android编辑文本下划线

为什么Java编译器为没有参数的方法(getter方法)创建桥接方法

具有 DayOfWeek 列表的 JPA 实体