多个线程编辑ArrayList及其对象

时间:2013-03-12 05:25:19

标签: java multithreading arraylist wait synchronized

我正在尝试运行2个并发线程,其中一个不断添加对象到列表,另一个更新这些对象,也可以从列表中删除其中的一些对象。 我有一个完整的项目,我已经将ArrayList用于我的方法和类,所以现在很难改变它。

我环顾四周,找到了一些方法,但正如我所说,很难从ArrayList改变。我尝试将synchronizednotify()用于将对象添加到列表中的方法,并尝试使用wait()来更改这些对象,并在符合特定条件的情况下删除它们。

现在,我已经想出了如何使用CopyOnWriteArrayList来做到这一点,但我想知道是否有可能使用ArrayList本身来模拟这个。所以我不必编辑我的整个代码。

所以,基本上,我想做这样的事情,但ArrayList

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

public class ListExample{
    CopyOnWriteArrayList<MyObject> syncList;

    public ListExample(){
        syncList = new CopyOnWriteArrayList<MyObject>();

        Thread thread1 = new Thread(){
            public void run(){
                synchronized (syncList){
                    for(int i = 0; i < 10; i++){
                        syncList.add(new MyObject(i));
                    }
                }
            }
        };

        Thread thread2 = new Thread(){
            public void run(){
                synchronized (syncList){
                    Iterator<MyObject> iterator = syncList.iterator();
                    while(iterator.hasNext()){
                        MyObject temp = iterator.next();

                        //this is just a sample list manipulation
                        if (temp.getID() > 3)
                            syncList.remove(temp);

                        System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
                    }
                }
            }
        };

        thread1.start();
        thread2.start();
    }

    public static void main(String[] args){
        new ListExample();
    }
}

class MyObject{
    private int ID;

    public MyObject(int ID){
        this.ID = ID;
    }

    public int getID(){
        return ID;
    }

    public void setID(int ID){
        this.ID = ID;
    }
}

我还阅读了关于Collections.synchronizedList(new ArrayList())但又一次,我相信这需要我更改我的代码,因为我有大量的方法将ArrayList作为参数。

任何指导都会受到赞赏,因为我没有想法。谢谢。

4 个答案:

答案 0 :(得分:4)

您可能对java.util.concurrent包提供的馆藏感兴趣。它们对于生产者/消费者场景非常有用,其中一个或多个线程将事物添加到队列中,而其他线程则使用它们。根据您是要阻塞还是在队列满/空时失败,有不同的方法。

关于重构方法,您应该使用接口(例如List)而不是具体的实现类(例如ArrayList)。这就是接口的目的,Java API有很好的支持。

答案 1 :(得分:1)

作为一种快速解决方案,您可以扩展ArrayList并使修改方法(添加/删除)同步。并重新分解代码以将ArrayList替换为您的custom-ArrayList

答案 2 :(得分:0)

使用Vector代替ArrayList。请记住将其存储在List引用中,因为Vector包含不推荐使用的方法。与ArrayList不同,Vector同步其内部操作,与CopyOnWriteArrayList不同,每次进行修改时都不会复制内部数组。

答案 3 :(得分:0)

当然你应该使用java.util.concurrent pakage。但是,让我们看一下仅ArrayList和同步发生/可能发生的事情。

在您的代码中,如果您只有ArrayList代替CopyOnWriteArrayList,那么它应该可以正常运行,因为您在线程中正在执行/操作的任何内容都提供了完全同步synchronized (syncList)。如果整个事情是同步的,你不需要任何wait() notify()(但不建议这样做)。

但是这段代码会给ConcurrentModificationException,因为一旦你使用了iterator syncList.iterator(),就不应该从该列表中删除元素,否则它会在迭代时产生不良结果,这就是为什么它设计为快速失败并给出例外。为避免这种情况,您可以使用:

                Iterator<MyObject> iterator = syncList.iterator();
                ArrayList<MyObject> toBeRemoved = new ArrayList<MyObject>();
                while(iterator.hasNext()){
                    MyObject temp = iterator.next();

                    //this is just a sample list manipulation
                    if (temp.getID() > 3)
                    {
                        //syncList.remove(temp);
                        toBeRemoved.add(temp);
                    }
                    System.out.println("Object ID: " + temp.getID() + " AND list size: " + syncList.size());
                }
                syncList.removeAll(toBeRemoved);

现在关于同步,你应该努力最小化它的范围,否则线程之间会有不必要的等待,这就是为什么java.util.concurrent包在多线程中具有高性能(甚至使用非阻塞算法)。或者您也可以使用Collections.synchronizedList(new ArrayList()),但它们不如concurrent类。

如果要在生产者/消费者问题中使用条件同步,则可以在同一对象(锁定)上使用wait() notify()机制。但是,已经有一些类可以帮助您使用java.util.concurrent.LinkedBlockingQueue