Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki MarkupAccording to Section 17.9, "Sleep and Yield" of the Java Language Specification \[[JLS 2005|AA. Java References#JLS 05]\]:to the Java Language Specification, §17.3, "Sleep and Yield" [JLS 2013],

It is important to note that neither Thread.sleep nor Thread.yield have any synchronization semantics. In particular, the compiler does not have to flush writes cached in registers out to shared memory before a call to Thread.sleep or Thread.yield, nor does the compiler have to reload values cached in registers after a call to Thread.sleep or Thread.yield.

...

Code that bases its concurrency safety on thread suspension or yields to processes that

  • Flush cached registers,
  • Reload any values,
  • Or provide any happens-before relationships when execution resumes,

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 a non-volatile Boolean 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.

Code Block
bgColor#FFCCCC

final class ControlledStop implements Runnable {
  private boolean done = false;

  @Override public void run() {
    while (!done) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // Reset interrupted status
        Thread.currentThread().interrupt(); }
    }
  }

  public void shutdown() {
    this.done = true;
  }
}

Wiki MarkupHowever, the compiler is free to read the field {{this.done}} once and reuse the cached value in each execution of the loop. Consequently, the while loop might not terminate, even if another thread calls the {{shutdown()}} method to change the value of {{this.done}} \[[JLS 2005|AA. Java References#JLS 05]\]. This error could have resulted from the programmer incorrectly assuming that the call to {{Thread.sleep()}} would cause cached values to be The compiler, in this case, 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 2013]. This error could have resulted from the programmer incorrectly assuming that the call to Thread.sleep() causes cached values to be reloaded.

Compliant Solution (Volatile Flag)

This compliant solution declares the flag field volatile to ensure that updates to it its value are made visible across multiple threads.:

Code Block
bgColor#ccccff

final class ControlledStop implements Runnable {
  private volatile boolean done = false;

  @Override public void run() {
    //...
  }

  // ...
}

The volatile flag keyword 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 up immediately and handle the interruption.

Code Block
bgColor#ccccff

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) {
    Threadth.currentThread().interrupt();
  }
}

Note that the interrupting thread must know which thread to interrupt; logic for tracking this relationship has been omitted from this solution.

Noncompliant Code Example (getState())

This noncompliant code example contains a doSomething() method that starts a thread. The thread supports interruption by checking a volatile flag and blocks waiting waits until notified. The stop() method notifies checks to see whether the thread if it is blocked on the wait and ; if so, it sets the flag to true and notifies the thread so that the thread can terminate.

Code Block
bgColor#FFCCCC

public class Waiter {
  private Thread thread;
  private volatile boolean flag;
  private final Object lock = new Object();

  public void doSomething() {
    thread = new Thread(new Runnable() {
      @Override public void run() {
        synchronized(lock) {
          while (!flag) {
            try {
              lock.wait();
              // ...
            } catch (InterruptedException e) {
              // Forward to handler
            }
          }
        }
      }
    });
    thread.start();
  }

  public boolean stop() {
    if (thread != null) {
      if (thread.getState() == Thread.State.WAITING) {
        flagsynchronized = true;(lock) {
        synchronized (lock) {  flag = true;
          lock.notifyAll();
        }
        return true;
      }
    }
    return false;
  }
}

...

Unfortunately, the {{stop()}} method incorrectly uses the {{Thread.getState()}} method to check whether the thread is blocked and has not terminated before delivering the notification. Using the {{Thread.getState()}} method for synchronization control , such as checking whether a thread is blocked on a wait , is inappropriate.This is true because a blocked thread is not always required to enter the {{WAITING}} or {{TIMED_WAITING}} state in cases where the JVM implements blocking using spin-waiting \[[Goetz 2006|AA. Java References#Goetz 06]\]. Because the thread may never enter the {{WAITING}} state, the {{stop()}} method may not terminate the threadinappropriate. Java Virtual Machines (JVMs) are permitted to implement blocking using spin-waiting; consequently, a thread can be blocked without entering the WAITING or TIMED_WAITING state [Goetz 2006]. Because the thread may never enter the WAITING state, the stop() method might fail to terminate the thread.

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() on a thread that is not blocked on a affects only threads that are blocked on an invocation of wait() invocation has no effect.:

Code Block
bgColor#ccccff

public class Waiter {
  // ...
  private Thread thread;
  private volatile boolean flag;
  private final Object lock = new Object();

  public boolean stop() {
    if (thread != null) {
      flagsynchronized = true;
(lock) {
        synchronizedflag (lock) {= 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.

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

THI00- J

low

probable

medium

P4

L3

References

Wiki Markup
\[[JLS 2005|AA. Java References#JLS 05]\] section 17.9 "Sleep and Yield"

Bibliography

 

...

Image Added Image Added Image Removed      12. Locking (LCK)      Image Modified