是否可以在将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方法正在做一些非常密集的事情,后续的提交已排队。
答案 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,然后使用更新的用户列表提交新任务。否则,您不仅会遇到通过线程篡改列表而导致的可见性问题,而且无法知道您是否正在修改已经运行的任务。