I want to submit a lambda, to an ExecutorService. The lambda is closing over an accumulating Arraylist, but I passed a defensive copy using "toArray" and then cleared the accumulator. It seems the lambda, is not capturing the response of "toArray".
But if I assign the copy to another reference outside the lambda, then its properly processed.
Original Code:
public class Test {
public static void main(String[] args) {
final int BATCH = 10;
ArrayList<Integer> accumulator = new ArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < BATCH * 10; i++) {
accumulator.add(i);
if (accumulator.size() >= BATCH) {
executorService.submit(() -> run(accumulator.toArray())); // faulty
accumulator.clear();
}
}
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.MINUTES)) {
System.err.println("Waited for 10 minutes, forced exit");
System.exit(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static void run(Object[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
Above doesn't print the full range of "i", I am guessing by the time the lambda's RHS is evaluated to call the accumulator.toArray(), the accumulator is changed.
Now if I pass a reference to the copy to the lambda it works.
final Object[] temp = accumulator.toArray();
executorService.submit(() -> run(temp));
accumulator.clear();
Edit:
What I am interested in is explanation of this behaviour ? Is my error because the function call (toArray) on the right hand side of a lambda is only evaluated when the full lambda is executed ?