Java和同步两个线程

Posted

技术标签:

【中文标题】Java和同步两个线程【英文标题】:Java and Synchronizing two threads 【发布时间】:2012-05-12 10:59:12 【问题描述】:

我有两个线程修改相同的对象。这些对象是ArrayList(不是向量)中的自定义、非同步对象。我想让这两个线程很好地协同工作,因为它们是同时被调用的。

这是线程 1 中唯一重要的方法。

public void doThread1Action() 
  //something...
  for(myObject x : MyArrayList)
    modify(x);
  

这是线程2中的一个方法:

public void doThread2Action()    
  //something...
  for(myObject x : MyArrayList)
    modifyAgain(x);
  

目前,在测试时,我偶尔会遇到`ConcurrentModificationExceptions`。 (我认为这取决于线程 1 在线程 2 尝试修改对象之前完成迭代的速度。)

我是否认为通过简单地将synchronized 附加到这两个方法的开头,线程将以同步的方式一起工作,而不是尝试访问ArrayList?还是应该将ArrayList 更改为Vector

【问题讨论】:

【参考方案1】:

ConcurrentModificationException 并非源于修改集合中的对象,而是源于在迭代器处于活动状态时从集合中添加/删除。

共享资源是集合,必须有第三种方法使用和添加/删除。要获得正确的并发性,您必须在访问它的所有方法中同步对集合资源的访问。

为避免同步块过长,一种常见的模式可能是将集合复制到同步块中,然后对其进行迭代。如果您这样做,请注意您首先谈论的问题(对象的并发修改)再次出现 - 但这次您可以锁定另一个资源。

【讨论】:

【参考方案2】:

您不需要同步对列表的访问,只要您不对其进行结构性修改,即只要您不从列表中添加或删除对象。你也不应该看到ConcurrentModificationExceptions,因为只有在你对列表进行结构性修改时才会抛出这些。

因此,假设您只修改列表中包含的对象,但不添加或删除或重新排序列表中的对象,则可以在修改时对包含的对象进行同步,如下所示:

void modifyAgain(MyObject x) 
    synchronized(x) 
        // do the modification
    

我不会在 modifyAgain() 方法上使用 synchronized 修饰符,因为这不允许同时修改列表中的两个不同对象。

另一个线程中的modify()方法当然必须和modifyAgain()一样实现。

【讨论】:

【参考方案3】:

您需要在同一个锁上同步访问集合,因此仅在方法上使用同步关键字(假设它们在不同的类中)将锁定两个不同的对象。

所以这里是你可能需要做的一个例子:

Object lock = new Object();

public void doThread1Action()

//something...
    synchronized(lock)
        for(myObject x : MyArrayList)
           modify(x);
    



public void doThread2Action()

//something...
    synchronized(lock)
        for(myObject x : MyArrayList)
            modifyAgain(x);
    


您也可以考虑使用CopyOnWriteArrayList 而不是Vector

【讨论】:

【参考方案4】:

我猜你的问题与ConcurrentModificationException 有关。这个类在它的 Java 文档中说:

/** * 检测到的方法可能会抛出此异常 并发 * 修改对象时不进行此类修改 允许的。

*/

在您的情况下,问题是列表中的迭代器并且可能会被修改。我想通过以下实现你的问题将是唯一的:

public void doThread1Action()

    synchronized(x //for sample)
    
       //something...
       for(myObject x : MyArrayList)
       
          modify(x);
       
    

然后:

public void doThread2Action()

    synchronized(x //for sample)
    
       //something...
       for(myObject x : MyArrayList)
       
          modifyAgain(x);
       
    

为了获得更好的结果,我希望有人纠正我的解决方案。

【讨论】:

以上是关于Java和同步两个线程的主要内容,如果未能解决你的问题,请参考以下文章

Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

Java同步—线程锁和条件对象

java并发传统线程同步通信技术

java线程之线程同步

java并发线程同步工具Exchanger的使用

Java线程:线程同步synchronized和volatile