遍历列表并将可调用对象提交给ExecutorService

时间:2018-09-28 16:17:37

标签: java multithreading

我有一个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变量应该具有正确的用户,对吧?

1 个答案:

答案 0 :(得分:1)

您的代码将按原样运行。定义匿名类时,将捕获在匿名类/ lambda中使用的变量-这就是Java要求它们有效地为最终变量的原因-因此您不必担心,user的值仍将相同当可调用对象运行时。