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

VNA02-J

Consider, for example, two threads that call toggle(). The expected effect of toggling flag twice is that it is restored to its original value. However, the following scenario leaves flag in the incorrect state:

Time

flag=

Thread

Action

 

 

 

 

1

true

t1

reads the current value of flag, true, into a temporary variable

2

true

t2

reads the current value of flag, (still) true, into a temporary variable

3

true

t1

toggles the temporary variable to false

 

 

 

 

4

true

t2

toggles the temporary variable to false

 

 

 

 

5

false

t1

writes the temporary variable’s value to flag

6

false

t2

writes the temporary variable’s value to flag

As a result, the effect of the call by t2 is not reflected in flag; the program behaves as if toggle() was called only once, not twice.

2.3.2Noncompliant Code Example (Bitwise Negation)

Similarly, the toggle() method can use the compound assignment operator ^= to negate the current value of flag.

final class Flag {

private boolean flag = true;

public void toggle() { // Unsafe

flag ^= true; // Same as flag = !flag;

}

public boolean getFlag() { // Unsafe return flag;

}

}

This code is also not thread-safe. A data race exists because ^= is a non-atomic compound operation.

2.3.3Noncompliant Code Example (volatile)

Declaring flag volatile does not help either:

final class Flag {

private volatile boolean flag = true;

public void toggle() { // Unsafe flag ^= true;

}

public boolean getFlag() { // Safe

CMU/SEI-2010-TR-015 | 17

VNA02-J

return flag;

}

}

This code remains unsuitable for multithreaded use because declaring a variable volatile does not guarantee the atomicity of compound operations on it.

2.3.4Compliant Solution (Synchronization)

This compliant solution declares both the toggle() and getFlag() methods as synchronized.

final class Flag {

private boolean flag = true;

public synchronized void toggle() { flag ^= true; // Same as flag = !flag;

}

public synchronized boolean getFlag() { return flag;

}

}

This guards the reads and writes to the flag field with a lock on the instance, that is, this. This compliant solution ensures that changes are visible to all the threads. Now, only two execution orders are possible, one of which is shown below.

Time

flag=

Thread

Action

1

true

t1

reads the current value of flag, true, into a temporary variable

2

true

t1

toggles the temporary variable to false

 

 

 

 

3

false

t1

writes the temporary variable's value to flag

4

false

t2

reads the current value of flag, false, into a temporary variable

5

false

t2

toggles the temporary variable to true

 

 

 

 

6

true

t2

writes the temporary variable's value to flag

The second execution order involves the same operations, but t2 starts and finishes before t1.

Compliance with guideline “LCK00-J. Use private final lock objects to synchronize classes that may interact with untrusted code” on page 41 can reduce the likelihood of misuse by ensuring that untrusted callers cannot access the lock object.

2.3.5Compliant Solution (Volatile-Read, Synchronized-Write)

In this compliant solution, the getFlag() method is not synchronized, and flag is declared volatile. This solution is compliant because the read of flag in the getFlag() method is an atomic operation and the volatile qualification assures visibility. The toggle() method still requires synchronization because it performs a non-atomic operation.

CMU/SEI-2010-TR-015 | 18

VNA02-J

final class Flag {

private volatile boolean flag = true;

public synchronized void toggle() { flag ^= true; // Same as flag = !flag;

}

public boolean getFlag() { return flag;

}

}

This approach may not be used when a getter method performs operations other than just returning the value of a volatile field without having to use any synchronization. Unless read performance is critical, this technique may not offer significant advantages over synchronization [Goetz 2006].

Guideline “VNA06-J. Do not assume that declaring an object reference volatile guarantees visibility of its members” on page 35 also addresses the volatile-read, synchronized-write pattern.

2.3.6Compliant Solution (Read-Write Lock)

This compliant solution uses a read-write lock to ensure atomicity and visibility.

final class Flag {

private boolean flag = true;

private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock();

private final Lock writeLock = lock.writeLock();

public synchronized void toggle() { writeLock.lock();

try {

flag ^= true; // Same as flag = !flag;

}finally { writeLock.unlock();

}

}

public boolean getFlag() { readLock.lock();

try {

return flag; } finally {

readLock.unlock();

}

}

}

CMU/SEI-2010-TR-015 | 19

VNA02-J

Read-write locks allow shared state to be accessed by multiple readers or a single writer but never both. According to Goetz [Goetz 2006]

In practice, read-write locks can improve performance for frequently accessed read-mostly data structures on multiprocessor systems; under other conditions they perform slightly worse than exclusive locks due to their greater complexity.

Profiling the application can determine the suitability of read-write locks.

2.3.7Compliant Solution (AtomicBoolean)

This compliant solution declares flag an AtomicBoolean type.

import java.util.concurrent.atomic.AtomicBoolean;

final class Flag {

private AtomicBoolean flag = new AtomicBoolean(true);

public void toggle() { boolean temp;

do {

temp = flag.get();

} while (!flag.compareAndSet(temp, !temp));

}

public AtomicBoolean getFlag() { return flag;

}

}

The flag variable is updated using the compareAndSet() method of the AtomicBoolean class. All updates are visible to other threads.

2.3.8Noncompliant Code Example (Addition of Primitives)

In this noncompliant code example, multiple threads can invoke the setValues() method to set the a and b fields. Because this class does not test for integer overflow, a user of the Adder class must ensure that the arguments to the setValues() method can be added without overflow.

(For more information, see guideline “INT00-J. Perform explicit range checking to ensure integer operations do not overflow.1”)

final class Adder { private int a; private int b;

public int getSum() { return a + b;

}

1

This guideline is described at https://www.securecoding.cert.org/confluence/display/java/.

 

CMU/SEI-2010-TR-015 | 20

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