dabadaba
dabadaba

Reputation: 9532

Stuck at an infinite loop unless printing result

I am trying to test a use case where I need to launch two threads, but the second one needs to wait for a particular state to happen.

The first thread launches the resolution process of a tournament in order to calculate its schedules. The second thread stops the resolution process.

@Test
public void stopResolutionProcessTest() throws InterruptedException {
    TournamentSolver solver = tournament.getSolver();

    Thread solveThread = new Thread(tournament::solve);
    solveThread.start();

    while (solver.getResolutionState() != TournamentSolver.ResolutionState.COMPUTING);

    Thread stopThread = new Thread(solver::stopResolutionProcess);
    stopThread.start();

    solveThread.join();
    stopThread.join();

    assertEquals(TournamentSolver.ResolutionState.INCOMPLETE, solver.getResolutionState());
}

The main thread gets stuck in the while loop, as if it were infinite.

However, if I just print the resolution state inside the loop, the test runs as expected:

while (solver.getResolutionState() != TournamentSolver.ResolutionState.COMPUTING)
    System.out.println(solver.getResolutionState());

I have no explanation for this; I feel like I am back several years in the past when I was studying concurrency and unexpected things I couldn't explain happened.

Could anybody shed some light on what's going on?

Edit: alright I set my getResolutionState() as synchronized and that did the trick, but I still don't understand why this helps or why printing the state would render the described results.

Upvotes: 2

Views: 106

Answers (1)

Solomon Slow
Solomon Slow

Reputation: 27190

What does getResolutionState() do? If all it's doing is returning the value of some variable that you're expecting the other thread to update, then it's possible that the other thread already has updated it, but that the update has not become visible to stopResolutionProcessTest().

If thread A sets some shared variable to a new value, the Java Language Specification does not require the new value to immediately become visible to any other thread. In fact, it does not require the new value to ever become visible until the threads synchronize with each other in some way that establishes a so-called "happens before" relationship.

You can find thousands of questions on StackOverflow that talk about "HappensBefore"

One operation that affects the visibility of updates is entering or leaving a synchronized block. The System.out.println(...) method uses synchronized blocks, so it would be no surprise if calling println() made a previously invisible update suddenly become visible.


If getResolutionState() is just a simple getter for a private variable, then one way to guarantee that the state change becomes visible to other threads is to declare it as a volatile variable.

Java guarantees that any update that one thread makes to a volatile variable (along with any other update that it made to any other variable before it touched the volatile) will become visible to any other thread that reads the same volatile variable.

Upvotes: 1

Related Questions