Анонимный внутренний класс в Groovy не фиксирует окончательное состояние внешней переменной - PullRequest
0 голосов
/ 28 сентября 2018

Я запускаю тест, в котором беру 1000 строк, которые представляют собой идентификатор.Я создаю ExecutorService с 100 потоками, зацикливаю 1000 строк и создаю Callable для каждого.Это прекрасно работает в Java, но я обнаружил в Groovy, что анонимный внутренний класс Callable не хранит повторное значение.

Вот мой тест:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class CallableTest {

    public static void main( String[] args ) throws InterruptedException, ExecutionException {
        CallableTest test = new CallableTest();

        test.runTest();
    }

    public List<String> setupTest(){

        List<String> ids = new ArrayList<String>();

        for(int i = 0; i < 1000; i++) {
            ids.add( "ID_" + i );
        }

        return ids;
    }

    public void runTest() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        List<Future<String>> futures = new ArrayList<Future<String>>();
        List<String> ids = setupTest();

        for(final String id : ids) {
            Future<String> future = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    return doSomethingWithId(id);
                }
            });

            futures.add( future );

        }

        for(Future<String> future : futures) {
            String message = future.get();
            System.out.println(message);
        }
    }

    public String doSomethingWithId(String id) {
        return "Doing something with ID: " + id;
    }
}

Проблема в Groovyздесь:

for(final String id : ids) {
    Future<String> future = executor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            // The ID value here is not the ID value from when the Callable was created 
            return doSomethingWithId(id);
        }
    });

    futures.add( future );

}

Как вы можете видеть в моем комментарии, значение id при вызове метода doSomethingWithId не совпадает со значением, которое было при создании Callable.Это приводит к тому, что Doing something with ID: ID_999 печатается большую часть времени.

Если я скопирую это в Java-проект и запустите его, он будет работать, как и ожидалось.Я получаю Doing something with ID: ID_x от 0-999 без дубликатов.

Почему это не работает в Groovy?Насколько я понимаю, Groovy должен поддерживать анонимные внутренние классы, но похоже, что анонимный внутренний класс Callable не захватывает состояние внешней переменной id, когда он создает анонимный класс.Я использую Java 7 с Groovy 2.3.10.

ОБНОВЛЕНИЕ Я обнаружил, что добавление следующего сделало эту работу:

for(final String id : ids) {

    final String myId = id // This makes it work

    Future<String> future = executor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return doSomethingWithId(myId);
        }
    });

    futures.add( future );

}

Похоже, Groovy нена самом деле сделать значение, возвращаемое из итератора final?

...