我有一个List
个用户要迭代,并为每个传递给Callable
的用户创建一个ExecutorService
,如下所示:
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);
}
我认为这将无法正常工作,因为在创建call()
时,Callable
方法内的用户不是当前迭代中的同一用户。换句话说:
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 );
}
这可行,但是我很好奇是否可以像以前一样使用MyCallable
作为匿名内部类而不是创建一个名为Callable
的新类。但是,我改为向其中添加一个成员变量。
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
变量应该具有正确的用户,对吧?
答案 0 :(得分:1)
您的代码将按原样运行。定义匿名类时,将捕获在匿名类/ lambda中使用的变量-这就是Java要求它们有效地为最终变量的原因-因此您不必担心,user
的值仍将相同当可调用对象运行时。