...
is incorrect and is consequently disallowed. Programs must ensure that communication between threads has proper synchronization, happens-before, and safe publication semantics.
Noncompliant Code Example (sleep()
)
This noncompliant code attempts to use the nonvolatile primitive Boolean member done
as a flag to terminate execution of a thread. A separate thread sets done
to true
by calling the shutdown()
method.
...
However, the compiler is free to read the field this.done
once and to reuse the cached value in each execution of the loop. Consequently, the while
loop might never terminate, even when another thread calls the shutdown()
method to change the value of this.done
[JLS 2011]. This error could have resulted from the programmer incorrectly assuming that the call to Thread.sleep()
would cause cached values to be reloaded.
Compliant Solution (Volatile Flag)
This compliant solution declares the flag field volatile
to ensure that updates to its value are made visible across multiple threads:
...
The volatile flag establishes a happens-before relationship between this thread and any other thread that sets done
.
Compliant Solution (Thread.interrupt()
)
A better solution for methods that call sleep()
is to use thread interruption, which causes the sleeping thread to wake immediately and handle the interruption. Note that the interrupting thread must know which thread to interrupt; logic for tracking this relationship has been elided.
Code Block | ||
---|---|---|
| ||
final class ControlledStop implements Runnable { @Override public void run() { // Record current thread, so others can interrupt it myThread = currentThread(); while (!Thread.interrupted()) { try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } public void shutdown(Thread th) { th.interrupt(); } } |
Noncompliant Code Example (getState()
)
This noncompliant code example contains a doSomething()
method that starts a thread. The thread supports interruption by checking a flag and blocks waiting until notified. The stop()
method checks to see whether the thread is blocked on the wait; if so, it sets the flag to true and notifies the thread so that the thread can terminate.
...
If doSomething()
and stop()
are called from different threads, the stop()
method could fail to see the initialized thread
, even though doSomething()
was called earlier, unless there is a happens-before relationship between the two calls. If the two methods are invoked by the same thread, they automatically have a happens-before relationship and consequently cannot encounter this problem.
Compliant Solution
This compliant solution removes the check for determining whether the thread is in the WAITING
state. This check is unnecessary because invoking notifyAll()
affects only threads that are blocked on a wait()
invocation.
Code Block | ||
---|---|---|
| ||
public class Waiter { // ... private Thread thread; private volatile boolean flag; private final Object lock = new Object(); public boolean stop() { if (thread != null) { synchronized (lock) { flag = true; lock.notifyAll(); } return true; } return false; } } |
Applicability
Relying on the Thread
class's sleep()
, yield()
, and getState()
methods for synchronization control can cause unexpected behavior.
Bibliography
...