如何创建Synchronized arraylist

时间:2010-09-28 15:24:17

标签: java multithreading synchronization

我已经像这样创建了synchronized arrayList

import java.text.SimpleDateFormat;
import java.util.*;


class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
   arrayList.add(i);
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

但是像这样得到例外

线程“Thread-1”中的异常java.util.ConcurrentModificationException

我的方法有什么不对吗?

7 个答案:

答案 0 :(得分:26)

Iterator的{​​{1}}未被(且无法)同步,您需要在迭代时手动同步列表(请参阅javadoc):

synchronizedList

另一种方法是使用CopyOnWriteArrayList代替synchronized(arrayList) { Iterator it=arrayList.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } 。它实现了写时复制语义,因此不需要同步。

答案 1 :(得分:11)

其他答案已经确定了问题:

  • 同步集合的迭代器未同步。实际上,它们只是包装类中的集合对象返回的迭代器。

  • 许多集合类(包括ArrayList)使用故障快速机制来检测迭代期间的并发修改。在各个类的javadoc中清楚地记录了这种行为。这就是你所看到的。

并非所有集合类都这样做。例如,许多java.util.Concurrent...集合类允许在迭代期间进行并发修改,但放宽了迭代序列的语义,以便修改的结果在对象中可能显而易见由迭代器返回。

Collections.synchronizedList()的javadoc解释了如何同步迭代器。基本上你这样做:

List list = Collections.synchronizedList(new ArrayList());
  ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

(旁白:通常认为做这样的事情会起作用是不安全的。理论上,同步列表可以使用私有锁对象,synchronized语句不会锁定并发修改。但是javadocs说这是在这种情况下要做的......所以它是安全的。)

这样做的问题是锁定集合会产生潜在的并发瓶颈。替代方法是使用写时复制数据结构,该数据结构在内部复制集合的相关部分。这种方法意味着迭代器看到了集合的快照。可以与迭代同时对集合进行修改,但迭代器不会看到它们。写时复制的问题在于修改可能要贵得多。

最终,您需要通过并发修改与实际需求来平衡不同集合类型的特征和成本。你能不能看到迭代器没有看到所有并发修改?

答案 2 :(得分:10)

考虑使用线程安全的CopyOnWriteArrayList。每次添加项目时,都会创建基础数组的新副本。但是,迭代器在创建迭代器后不会反映列表的添加内容,但保证不会抛出ConcurrentModificationException

arrayList=new CopyOnWriteArrayList();

答案 3 :(得分:3)

在迭代同一个集合时操作(添加,删除)集合时会发生java.util.ConcurrentModificationException。

您可能希望在第一个线程创建第二个线程后使用第二个线程中的创建条目。因此,您可以使用ArrayLists get( index )和大小()进行控制

答案 4 :(得分:2)

正如Spike所说,你不能在迭代时修改它。但是,我认为解决方案是在迭代时锁定列表。

class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
synchronized(someLock) {
   arrayList.add(i);
}
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
synchronized(someLock) {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
}
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

我不确定你要做什么,所以我希望这不会破坏你代码的功能。

答案 5 :(得分:0)

您不能修改正在迭代的Collection。您可以通过索引而不是通过Iterator访问数组条目来解决此问题。如果您告诉我您尝试使用此代码解决的问题,我可以提供更多建议。

答案 6 :(得分:0)

让我们采用一个普通的列表(由ArrayList类实现)并使其同步。这显示在SynchronizedArrayList类中。     我们传递Collections.synchronizedList方法一个新的字符串ArrayList。该方法返回一个同步的字符串列表。     //这是SynchronizedArrayList类     包com.mnas.technology.automation.utility;     import java.util.ArrayList;     import java.util.Collections;     import java.util.Iterator;     import java.util.List;     import org.apache.log4j.Logger;     / **     *     * @author manoj.kumar     * @email kumarmanoj.mtech@gmail.com     *     * /     公共类SynchronizedArrayList {     static Logger log = Logger.getLogger(SynchronizedArrayList.class.getName());     public static void main(String [] args){

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<String>());
synchronizedList.add("Aditya");
synchronizedList.add("Siddharth");
synchronizedList.add("Manoj");

// when iterating over a synchronized list, we need to synchronize access to the synchronized list
synchronized (synchronizedList) {
Iterator<String> iterator = synchronizedList.iterator();
while (iterator.hasNext()) {
log.info("Synchronized Array List Items: " + iterator.next());
}
}

}
}

Notice that when iterating over the list, this access is still done using a synchronized block that locks on the synchronizedList object. 
In general, iterating over a synchronized collection should be done in a synchronized block