【问题标题】:Why Unsynchronized ArrayList Object add method is not behaving properly为什么不同步的 ArrayList 对象添加方法行为不正常
【发布时间】:2016-06-03 13:21:29
【问题描述】:

这里是示例代码

1) ArrayList 是一个单独的对象,它被传递给 ThreadPool 的每个线程。

2) 在执行结束时列表大小应该是 50,如果您检查示例输出,它可能不是 50。有时它可能是 41 或 47,为什么会这样。

public class Test {

    ArrayList list=new ArrayList();
    public static void main(String[] args) {
        ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
        Test test=new Test();
        for(int i=0;i<5;i++)
        {
            Mythread t1=new Mythread(test.list);

            executorService3.execute(t1);
        }

        executorService3.shutdown();

        while(executorService3.isShutdown())
        {
            //---This is not giveging proper output as expected is 50.--
            System.out.println("List size="+test.list.size());
            break;
        }
    }
}


class Mythread implements Runnable {

    List list=null;
    Mythread(List list) {
        this.list=list;
    }

    @Override
    public void run() {
        for(int i=0;i<10;i++) {
            this.list.add(i);   
        }
    }
}

【问题讨论】:

  • 您的Mythread 类不是线程。在其名称中看到“线程”令人困惑。您提交给ExecutorService 的事物的通用名称是“任务”。 MyTask 将是该类的更好名称。
  • 问:main() 例程中的while 循环应该完成什么? executorService3.isShutdown() 将在第一次和每次调用时返回 true,因为您的主线程在上面的行中关闭了服务。也许你应该使用executorService3.awaitTermination()
  • 问:你的意思是在你的问题标题中写“异步”吗?还是那是一个错字?您的线程在访问List 时确实使用同步,但“异步”是一种非常规的编写方式。大多数人会说“不同步”或“不同步”。不同之处在于,“异步”听起来像是您主动做了一些事情来删除同步,而“不同步”则更被动(即,您忽略了它。)
  • Re, "At end of execution list size should be 50" 实际上,由于您的代码允许多个线程在不同步的情况下更新列表,因此您应该期待这样的结果.如果您更改代码以在检查列表长度之前等待所有任务终止,您可能会得到该结果,但不能保证获得该结果。
  • 这个程序应该会导致邪恶的事情发生,因为它正在修改一个没有任何同步的列表。你应该预期结果会很糟糕。

标签: java multithreading arraylist add


【解决方案1】:

您的代码没有等待线程完成执行。当您的代码调用以下行时

System.out.println("List size="+test.list.size());

无法保证他们已经完成,因此无法保证列表包含预期的 50 项。使用 awaitTermination 方法(https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#awaitTermination(long,%20java.util.concurrent.TimeUnit)),例如:

executorService3.shutdown();
executorService3.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("List size="+test.list.size());

(为简洁省略异常处理)

【讨论】:

  • 我的感觉完全一样,但是在这种情况下,如果您查看我的代码 while(executorService3.isShutdown()) // 它在关闭时返回 true 对吗? { }
  • 嗨 Gothytim,我尝试过 executorService3.awaitTermination(1, TimeUnit.SECONDS);通过增加等待时间,但仍然得到相同的意外 ArrayList SIZE
【解决方案2】:

正如Javadoc for ArrayList 中所说:

请注意,此实现不同步。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表,则它必须在外部同步。

所以它“行为不正常”,因为您没有按照文档中的说明使用它。

按照 Javadoc 中的建议,您可以将 list 包装在同步列表中:

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

【讨论】:

  • 我喜欢这个答案,谢谢安迪·特纳。结论是:-- 当 2 个或更多线程尝试同时修改列表结构时,该列表应该是“同步的”
  • 这正是文档中引用的内容,所以,是的。除了它是“至少一个”,而不是“2 个或更多”这一事实之外。
  • 阅读报价单。都在里面。
  • “至少一个”是一个数学术语,意思是一个或多个。声明是“至少有一个线程修改”。我的问题是如果一个线程需要同步。
  • 我明白了,不是“至少有一个线程修改了”,而是“至少有一个线程修改了”
【解决方案3】:

这是一个并发问题。 在我看来,你有: 5 个线程将在同一个对象上执行 run 方法。多个线程可以在数组列表中的同一位置插入一个变量,因为它不同步。 可以打印一下列表的内容吗?

【讨论】:

  • 嗨 Bob 感谢您的回复,我认为您错了,“多线程可以增加 i 变量”---这里 i 变量在 Mythread 类的 run() 方法中,以及 Mythread 对象创建在循环的主要方法中。
猜你喜欢
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-05
相关资源
最近更新 更多