我有两个线程修改相同的对象。对象是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
?
答案 0 :(得分:3)
ConcurrentModificationException不是源于修改集合中的对象,而是源于在迭代器处于活动状态时从集合中添加/删除。
共享资源是集合,必须有第三种方法使用和添加/删除。要获得正确的并发性,必须在访问它的所有方法中同步对集合资源的访问。
为了避免过长的同步块,常见的模式可能是在同步块中复制集合,然后迭代它。如果你这样做,请注意你首先讨论的问题(并发修改你的对象)再次就位 - 但这次你可以锁定另一个资源。
答案 1 :(得分:0)
只要不从结构上修改它,即只要不从列表中添加或删除对象,就不需要同步对列表的访问。您也不应该看到ConcurrentModificationException
,因为只有在您从结构上修改列表时才会抛出这些内容。
因此,假设您只修改列表中包含的对象,但是您没有在列表中添加或删除或重新排序对象,则可以在修改它们时对包含的对象进行同步,如下所示:
void modifyAgain(MyObject x) {
synchronized(x) {
// do the modification
}
}
我不会在synchronized
方法上使用modifyAgain()
修饰符,因为它不允许同时修改列表中的两个不同对象。
其他线程中的modify()
方法当然必须以与modifyAgain()
相同的方式实现。
答案 2 :(得分:0)
您需要对同一个锁上的集合进行sychronsize访问,因此只需在方法上使用synchronized关键字(假设它们位于不同的类中)就会锁定两个不同的对象。
所以这里有一个你可能需要做的例子:
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
答案 3 :(得分:0)
我猜您的问题与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);
}
}
}
为了获得更好的结果,我希望任何人都能纠正我的解决方案。