-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathDoubleCheckedLockingShared.qhelp
More file actions
86 lines (74 loc) · 3.03 KB
/
DoubleCheckedLockingShared.qhelp
File metadata and controls
86 lines (74 loc) · 3.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Double-checked locking is a common pattern for lazy initialization of a field
accessed by multiple threads.
Depending on the memory model of the underlying runtime, it can, however, be
quite difficult to implement correctly, since reorderings performed by
compiler, runtime, or CPU might expose un-initialized or half-way initialized
objects to other threads.
Java has since version 5 improved its memory model to support double-checked
locking if the underlying field is marked <code>volatile</code> and if all
initialization happens before the volatile write.
</p>
</overview>
<recommendation>
<p>
First, it should be considered whether the getter that performs the lazy
initialization is performance critical.
If not, a much simpler solution is to completely avoid double-checked locking
and simply mark the entire getter as <code>synchronized</code>.
This is much easier to get right and guards against hard-to-find concurrency bugs.
</p>
<p>
If double-checked locking is used, it is important that the underlying field is
<code>volatile</code> and that the update to the field is the last thing that
happens in the synchronized region, that is, all initialization must be done
before the field is assigned.
Furthermore, the Java version must be 5 or newer.
Reading a <code>volatile</code> field has a slight overhead, so it is also
useful to use a local variable to minimize the number of volatile reads.
</p>
</recommendation>
<example>
<p>
The following code lazily initializes <code>f</code> to <code>new MyObject()</code>.
</p>
<sample src="DoubleCheckedLockingBad1.java"/>
<p>
This code is not thread-safe as another thread might see the assignment to
<code>f</code> before the constructor finishes evaluating, for example if the
compiler inlines the memory allocation and the constructor and reorders the
assignment to <code>f</code> to occur just after the memory allocation.
</p>
<p>
Another example that also is not thread-safe, even when <code>volatile</code>
is used, is if additional initialization happens after the assignment to
<code>f</code>, since then other threads may access the constructed object
before it is fully initialized, even without any reorderings by the compiler or
runtime.
</p>
<sample src="DoubleCheckedLockingBad2.java"/>
<p>
The code above should be rewritten to both use <code>volatile</code> and finish
all initialization before <code>f</code> is updated. Additionally, a local
variable can be used to avoid reading the field more times than neccessary.
</p>
<sample src="DoubleCheckedLockingGood.java"/>
</example>
<references>
<li>
<a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html">The "Double-Checked Locking is Broken" Declaration</a>.
</li>
<li>
Java Language Specification:
<a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">17.4. Memory Model</a>.
</li>
<li>
Wikipedia: <a href="https://en.wikipedia.org/wiki/Double-checked_locking">Double-checked locking</a>.
</li>
</references>
</qhelp>