Reputation: 6529
I have been experimenting with lambdas and have run into a problem I don't understand. The following code works fine in isolation, but somehow running this in a multi-threaded environment (that I don't control) causes run()
to be in one instance and doA()
to be in a different instance, which I can see as different instance IDs in the debugger.
public class Main {
private String string;
public static void main(String[] args) {
Main main = new Main();
Main main2 = new Main();
main2.run();
}
private interface Stepper {
boolean execute();
}
Stepper[] steps = { () -> { return doA(); }, () -> { return doB(); }, () -> { return doC(); } };
public boolean doA() { System.out.println(string); return true; }
public boolean doB() { System.out.println(string); string = "foo"; return true; }
public boolean doC() { System.out.println(string); return true; }
private void run() {
string = "changed";
for (Stepper step : steps) {
if (step.execute() == false) {
return;
}
}
}
}
The real class implements a proprietary descendant of Runnable
, which is not shown here. I moved the initialization of steps
into the run()
method, and it started working properly. How could having the array outside the run()
method cause this?
Upvotes: 1
Views: 473
Reputation: 4921
If you change your Stepper
interface, you could do something like this:
public class Main {
private String string;
public static void main(String[] args) {
Main main = new Main();
Main main2 = new Main();
main2.run();
}
private interface Stepper extends Function<Main, Boolean> {
default boolean execute(Main main){
return this.apply(main);
}
}
Stepper[] steps = {Main::doA, Main::doB, Main::doC};
public boolean doA() { System.out.println(string); return true; }
public boolean doB() { System.out.println(string); string = "foo"; return true; }
public boolean doC() { System.out.println(string); return true; }
private void run() {
string = "changed";
for (Stepper step : steps) {
if (step.execute(this) == false) {
return;
}
}
}
}
This works because the method references are static, but passing in this
to the execute function ensures that the current instance is always the one that will be acted upon.
If you've got a multithreaded environment, there's nothing stopping another thread from simultaneously running and interleaving with the output from this instance. I suggest that you gather your output into a StringBuilder
and print it out as one operation to ensure that the output isn't interleaved. If the order's not important then fair enough, but it'll sure make debugging easier to know that each set of output is from the same thread. You might even consider appending the thread ID to your output lines.
Upvotes: 1