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

TSM02-J

6.3.4Exceptions

TSM02-EX1: It is permissible to start a background thread during class initialization provided the thread does not access any fields. For example, the ObjectPreserver class (based on Patterns in Java [Grand 2002]) shown below provides a mechanism for storing object references, which prevents an object from being garbage-collected, even if the object is not de-referenced in the future.

public final class ObjectPreserver implements Runnable {

private static final ObjectPreserver lifeLine = new ObjectPreserver();

private ObjectPreserver() {

Thread thread = new Thread(this); thread.setDaemon(true);

thread.start(); // Keep this object alive

}

//Neither this class nor HashMap will be garbage-collected.

//References from HashMap to other objects will also exhibit this property private static final ConcurrentHashMap<Integer, Object> protectedMap

=new ConcurrentHashMap<Integer, Object>();

public synchronized void run() { try {

wait();

}catch (InterruptedException e) { Thread.currentThread().interrupt(); // Reset interrupted status

}

}

//Objects passed to this method will be preserved until

//the unpreserveObject() method is called

public static void preserveObject(Object obj) { protectedMap.put(0, obj);

}

//Returns the same instance every time public static Object getObject() {

return protectedMap.get(0);

}

//Unprotect the objects so that they can be garbage-collected public static void unpreserveObject() {

protectedMap.remove(0);

}

}

CMU/SEI-2010-TR-015 | 160

TSM02-J

This is a singleton class. (See guideline “MSC16-J. Address the shortcomings of the Singleton design pattern”13 for more information on how to defensively code singleton classes.) The initialization involves creating a background thread using the current instance of the class. The thread waits indefinitely by invoking Object.wait(). Consequently, this object persists for the remainder of the JVM’s lifetime. Because the object is managed by a daemon thread, the thread does not hinder a normal shutdown of the JVM.

While the initialization does involve a background thread, that thread does not access any fields or create any liveness or safety issues. Consequently, this code is a safe and useful exception to this guideline.

6.3.5Risk Assessment

Starting and using background threads during class initialization can result in deadlock conditions.

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

TSM02- J

low

probable

high

P2

L3

6.3.6References

[Bloch 2005b]

 

[Grand 2002]

Chapter 5, “Creational Patterns, Singleton”

13

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

 

CMU/SEI-2010-TR-015 | 161

TSM03-J

6.4TSM03-J. Do not publish partially initialized objects

During initialization of a shared object, the object must only be accessible to the thread constructing it. However, the object can be published safely (that is, made visible to other threads) once it is initialized. The JMM allows multiple threads to observe the object after its initialization has begun, but before it has concluded. Consequently, it is important to ensure that a partially initialized object is not published.

This guideline prohibits publishing a reference to a partially initialized member object instance before initialization has concluded. Guideline “TSM01-J. Do not let the “this” reference escape during object construction” on page 149 prohibits the this reference of the current object from escaping.

6.4.1Noncompliant Code Example

This noncompliant code example constructs a Helper object in the initialize() method of the Foo class. The Helper object’s fields are initialized by its constructor.

class Foo {

private Helper helper;

public Helper getHelper() { return helper;

}

public void initialize() { helper = new Helper(42);

}

}

public class Helper { private int n;

public Helper(int n) { this.n = n;

}

// ...

}

If a thread accesses helper using the getHelper() method before the initialize() method has been called, the thread will observe an uninitialized helper field. Later, if one thread calls initialize() and another calls getHelper(), the second thread might observe one of the following:

the helper reference as NULL

a fully initialized Helper object with the n field set to 42

a partially initialized Helper object with an uninitialized n that contains the default value 0

CMU/SEI-2010-TR-015 | 162

TSM03-J

In particular, the JMM permits compilers to allocate memory for the new Helper object and assign it to the helper field before initializing it. In other words, the compiler can reorder the write to the helper instance field with the write that initializes the Helper object (that is,

this.n = n) such that the former occurs first. This exposes a race window during which other threads may observe a partially initialized Helper object instance.

There is a separate issue: if two threads call initialize(), two Helper objects are created. This is a performance issue and not a correctness issue because n will be properly initialized and the unused Helper objects will be garbage-collected.

6.4.2Compliant Solution (Synchronization)

The publication of partially constructed object references can be prevented by using method synchronization, as shown in this compliant solution.

class Foo {

private Helper helper;

public synchronized Helper getHelper() { return helper;

}

public synchronized void initialize() { helper = new Helper(42);

}

}

Synchronizing both methods guarantees that they will not execute concurrently. If one thread calls initialize() just before another thread calls getHelper(), the synchronized initialize() method will always finish first. The synchronized keyword establishes a happens-before relationship between the two threads. This guarantees that the thread calling getHelper() sees the fully initialized Helper object or none at all (that is, helper contains a null reference). This approach guarantees proper publication for both immutable and mutable members.

CMU/SEI-2010-TR-015 | 163

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