【问题标题】:Multiple Threads executing same Method (Non Synchronized) behavior多个线程执行相同的方法(非同步)行为
【发布时间】:2017-10-23 00:19:58
【问题描述】:

我调用了一个从多个线程向列表添加项目的方法,该方法不同步

static List<String> list = new ArrayList<String>();

static void addItem(int itemNo){
    
    list.add("item "+itemNo);
}
public static void main(String[] args) {
    

    ExecutorService executor = Executors.newFixedThreadPool(4);
    for (int i = 0; i < 10; i++) 
    {
        int itemNo = i;
        Runnable task = () -> addItem(itemNo);
        executor.execute(task);
    }
    
    executor.shutdown();
    
    
    System.out.println(list);
    
}

打印列表的输出是随机的,每次我执行这段代码

我就是

[null, 第 1 项, 第 4 项, 第 5 项, 第 6 项, 第 7 项, 第 8 项, 第 9 项]

[第 1 项,空,第 2 项,第 4 项,第 5 项,第 3 项,第 6 项,第 7 项,第 8 项,第 9 项]

[第 1 项、第 2 项、第 3 项、第 4 项、第 5 项、第 6 项、第 7 项、第 8 项、第 9 项]

在第 1 和第 2 输出中,有空项目,第 1 在第 1 输出中,第 2 在第 2 中。而且列表的大小也不一样。

据我了解,多线程中的项目不会按顺序排列。我不知道在添加数组时项目有时会为空或跳过

我想了解这种行为,为什么将 null 项添加到数组中,以及为什么在我的方法未同步

时跳过项

您的宝贵回答对我有很大帮助, 提前致谢

【问题讨论】:

    标签: java multithreading arraylist synchronization executorservice


    【解决方案1】:

    List.add(Object) 函数不是原子的。也就是说,如果多个线程试图将一个元素添加到同一个列表中,它们可能会相互干扰并破坏 add 函数的内部工作(调整内部数据结构的大小,计算列表中的项目位置,...... )。这可能会导致意外输出甚至严重问题(例如,在没有正确同步的情况下跨线程使用 HashMap 时 - 即使对于只读操作也是如此)。

    如果您使用同步的List

     static List<String> list = Collections.synchronizedList(new ArrayList<String>());
    

    这将对list 实例synchronized 进行方法调用,从而添加一个有效的原子操作。通过此更改,仍然不会按顺序添加项目,但至少可以确保正确添加所有项目。

    【讨论】:

      【解决方案2】:

      因为添加项目不是原子操作,它需要多个操作,例如增加大小并将值分配给数组位置。

      在多线程竞态条件下,有时两个线程可能同时增加,所以它只增加一次,而不是两次,因此最终大小小于 10。

      有时两个线程在分配值之前都会按顺序递增,因此跳过一个位置,两个线程都分配到相同的位置,一个线程覆盖另一个线程的值,因此一个空值和一个缺失值。

      简而言之,对象的多线程使用,如ArrayList,将损坏列表。永远不要这样做。为什么你看到你所做的结果并不重要,只是不要这样做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多