在将可运行对象提交给ExecutorService后修改它?

时间:2016-07-26 19:58:35

标签: java multithreading java.util.concurrent

是否可以在将runnable对象提交给执行程序服务(具有无界队列的单个线程)之后对其进行修改?

例如:

public class Test {
    @Autowired
    private Runner userRunner;

    @Autowired
    private ExecutorService executorService;

    public void init() {
        for (int i = 0; i < 100; ++i) {
             userRunner.add("Temp" + i);
             Future runnerFuture = executorService.submit(userRunner);
        }
    }
}

public class Runner implements Runnable {
    private List<String> users = new ArrayList<>();

    public void add(String user) {
        users.add(user);
    }

    public void run() {
       /* Something here to do with users*/   
    }
}

正如您在上面的示例中所看到的,如果我们提交一个runnable对象并在循环内修改对象的内容,那么第一次提交到executor服务将使用新添加的用户。考虑到run方法正在做一些非常密集的事情,后续的提交已排队。

2 个答案:

答案 0 :(得分:1)

  

如果我们提交一个可运行的对象并在循环内修改对象的内容,那么第一次提交给执行者服务将使用新添加的用户。

仅当users ArrayList正确同步时。你正在做的是尝试从两个不同的线程修改users字段,这可能导致异常和其他不可预测的结果。同步确保互斥,因此多个线程不会意外地同时更改ArrayList,以及内存同步,以确保另一个线程的修改被看到。

您可以做的是为您的示例添加同步:

public void add(String user) {
    synchronized (users) {
        users.add(user);
    }
}
...

public void run() {
   synchronized (users) {
       /* Something here to do with users*/
   }
}

另一种选择是同步列表:

// you can't use this if you are iterating on this list (for, etc.)
private List<String> users = Collections.synchronizedList(new ArrayList<>());

但是,如果您在列表中使用for循环或以其他方式迭代它,则需要手动同步。

答案 1 :(得分:0)

最干净,最直接的方法是在Future上调用cancel,然后使用更新的用户列表提交新任务。否则,您不仅会遇到通过线程篡改列表而导致的可见性问题,而且无法知道您是否正在修改已经运行的任务。