遍历列表并将可调用对象提交到执行器服务



我有一个用户List,我想迭代并为每个用户创建一个传递给ExecutorServiceCallable,如下所示:

ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<String>> futures = new ArrayList<Future<String>>();
for(final User user : users) {
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return doSomethingWithUser(user);
}
});
futures.add( future );      
}
executor.shutdown();
for(Future<String> future : futures) {
String message = future.get();
System.out.println(message);        
}   

我相信这不会正常工作,因为创建Callable时,call()方法中的用户与迭代中的当前用户不同。换句话说:

for(final User user : users) {
System.out.println("User: " + user.username); // The current user
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("User: " + user.username); // Might not be the same user as above
return doSomethingWithUser(user);
}
});
futures.add( future );      
}

为了解决这个问题,我创建了一个MyCallable类来实现Callable并允许构造函数

public abstract class MyCallable<I, O> implements Callable<O> {
private I param;
public MyCallable(I param) {
this.param = param;
}
public I getParam() {
return param;
}
}

然后在我的迭代器中,我将用户参数传递给构造函数

for(final User user : users) {
Future<String> future = executor.submit(new MyCallable<User, String>(user) {
@Override
public String call() throws Exception {
return doSomethingWithUser(user);
}
});
futures.add( future );                  
}

这有效,但我很好奇是否可以像以前一样将Callable用作匿名内部类,而不是创建一个名为MyCallable的新类。但是,我会向它添加一个成员变量。

for(final User user : users) {
Future<String> future = executor.submit(new Callable<String>() {
private User _user = user;
@Override
public String call() throws Exception {
return doSomethingWithUser(_user);
}
});
futures.add( future );      
}

这行得好吗?我已经测试过它,它似乎有效,但是使用多线程可能很难知道。成员变量在构造函数之前初始化,所以理论上我的_user变量应该有正确的用户,对吧?

您的代码将按照原始方式正常工作。匿名类/lambda 中使用的变量在定义匿名类时被捕获 - 这就是为什么 Java 要求它们实际上是最终的 - 所以你不必担心,当可调用对象运行时,user的值仍然相同。

最新更新