【问题标题】:Multiple threads editing an ArrayList and its objects多个线程编辑 ArrayList 及其对象
【发布时间】:2013-02-27 12:50:25
【问题描述】:

我正在尝试运行 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 作为参数的方法。

任何指导将不胜感激,因为我没有想法。谢谢。

【问题讨论】:

  • 尽早重构,经常重构是你想要遵循的口头禅。最好现在而不是以后经历痛苦,将代码更改为使用接口List,而不是具体实现ArrayList
  • 请注意 - 您应该始终使用需要功能的最低通用对象。如果您不需要ArrayList 实际带来的任何功能,您应该考虑使用List 作为您的方法参数。这将为您的应用程序打开更广泛的实现基础 - 只是一个想法
  • 您似乎正在尝试解决生产者-消费者问题。尝试用谷歌搜索解决生产者-消费者问题

标签: java multithreading arraylist wait synchronized


【解决方案1】:

当然你应该使用java.util.concurrent 包。但是,让我们看看仅使用 ArrayList 和同步会发生什么/可能发生什么。

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

但是这段代码会给出ConcurrentModificationException,因为一旦你使用了迭代器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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-27
        • 2015-02-12
        • 2018-04-19
        • 2015-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多