Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java concurrency guidelines.pdf
Скачиваний:
16
Добавлен:
23.05.2015
Размер:
1.35 Mб
Скачать

THI00-J

4 Thread APIs (THI) Guidelines

4.1THI00-J. Do not assume that the sleep(), yield(), or getState() methods provide synchronization semantics

According to Section 17.9, “Sleep and Yield” of the Java Language Specification [Gosling 2005]

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.

Incorrectly assuming that thread suspension and yielding do any of the following can result in unexpected behavior:

flush the cached registers

reload any values

provide any happens-before relationships when execution resumes

4.1.1Noncompliant Code Example (sleep())

This noncompliant code example attempts to use a non-volatile Boolean done as a flag to terminate the execution of a thread. A separate thread sets done to true by calling the shutdown() method.

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

}

}

}

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

}

}

However, 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 [Gosling 2005]. This

CMU/SEI-2010-TR-015 | 91

THI00-J

error could have resulted from the programmer incorrectly assuming that the call to Thread.sleep() would cause cached values to be reloaded.

4.1.2Compliant Solution (Volatile Flag)

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

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

// ...

}

The volatile flag establishes a happens-before relationship between this thread and any other thread that sets done.

4.1.3Compliant 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.

final class ControlledStop implements Runnable { @Override public void run() {

while (!Thread.interrupted()) { try {

Thread.sleep(1000);

} catch (InterruptedException e) { Thread.currentThread().interrupt();

}

}

}

public void shutdown() { Thread.currentThread().interrupt();

}

}

4.1.4Noncompliant Code Example (getState())

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

public class Waiter { private Thread thread;

private volatile boolean flag;

private final Object lock = new Object();

CMU/SEI-2010-TR-015 | 92

THI00-J

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) { flag = true;

synchronized (lock) { 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]. Because the thread may never enter the WAITING state, the stop() method may not terminate the thread.

CMU/SEI-2010-TR-015 | 93

THI00-J

4.1.5Compliant 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 wait() invocation has no effect.

public class Waiter {

// ...

public boolean stop() { if (thread != null) {

flag = true; synchronized (lock) {

lock.notifyAll();

}

return true;

}

return false;

}

}

4.1.6Risk Assessment

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

4.1.7References

[Gosling 2005] Section 17.9, “Sleep and Yield”

CMU/SEI-2010-TR-015 | 94

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]