javamemory$model$ - ut · memory$wall$ • 1980 – 1$cpu$operaon$~1$memory$access$operaon$ •...
TRANSCRIPT
Java Memory Model
when you think you know Java…
If programmers don’t know what their code is doing, programmers won’t be able to know what their code is doing wrong.
Requirements for Programming Language Memory Models Jeremy Manson and William Pugh
Agenda
• Why JMM? • JMM properCes – Atomicity – Visibility – Reordering
• VolaCle • Final fields
Memory wall
• 1980 – 1 CPU operaCon ~ 1 memory access operaCon
• 2010 – CPU speed increased ~10000 Cmes – Memory speed increased ~10 Cmes L
• 2005 “The Free Lunch Is Over” – hVp://www.gotw.ca/publicaCons/concurrency-‐ddj.htm
Consequences
• Complex architecture of the modern CPUs – MulCcore – Out-‐of-‐order execuCon – Store buffers – Hierarchical caches – Cache coherence protocols – NUMA
Java Memory Model
• Specifies minimal guarantees given by JVM
• Key principles: – All threads share the main memory – Each thread uses a local working memory – Flushing or refreshing working memory to/from main memory must comply to JMM rules
T1 T2 T3
working memory
working memory
working memory
main memory
It is all about proper
synchroniza9on!
Safety issues in mulCthreaded systems
• Many intuiCve assumpCons do no hold • Some widely used idioms do not hold – Double-‐checked locking idiom – Checking non-‐volaCle flag for thread terminaCon
• Can’t depend on tesCng to check errors – Some anomaliCes will occur only on some plaforms
– AnomaliCes will occur randomly
Taxonomy
• High level concurrency abstracCons – java.uCl.concurrent
• Low level locking – synchronized blocks & java.uCl.concurrent.locks
• Low level primiCves – volaCle, java.uCl.concurrent.atomic
• Data races: deliberate undersynchronizaCon – Don’t try it at home!
SynchronizaCon is needed for blocking and visibility
• SynchronizaCon isn’t just about mutual exclusion and blocking
• It also regulates when other threads must see writes by other threads – When writes become visible
• Without synchronizaCon, compiler and processor are allowed to reorder memory accesses in ways that may surprise you – And break your code
x = y = 0
x = 1
j = y
y = 1
i = x
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
y = 1
i = x
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
y = 1
i = x
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
i = x
y = 1
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
i = x
y = 1
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
i = x
y = 1
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
x = y = 0
x = 1
j = y
i = x
y = 1
Thread 1 Thread 2
Can this result in i=0 and j=0?
start threads
Yes!
How can this happen?
• Compiler can reorder statements – Or keep values in registers
• Processor can reorder statements • On mulC-‐processor, values not synchronized to global memory
• The memory model is designed to allow aggressive opCmizaCon
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2 Everything before
an unlock
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2 Everything before
an unlock
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2 Everything before
an unlock
Is visible to everything aHer a later lock on the same Object
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2 Everything before
an unlock
Is visible to everything aHer a later lock on the same Object
ref1. x = 1
lock M
glo = ref1
unlock M
When are acCons visible to other threads?
lock M
ref2 = glo
unlock M
j = ref2.x Thread 1
Thread 2 Lock release & subsequent acquire
Release and acquire
• All memory accesses before a release – Are ordered before and visible to any memory accesses ajer matching acquire
• Unlocking a monitor/lock is a release – That is acquired by any following lock of that monitor/lock
…
write(M1)
…
Visibility between threads
…
read(M1)
… Thread 1
Thread 2
…
write(M1)
…
Visibility between threads
…
read(M1)
… Thread 1
Thread 2
Same monitor!
…
write(V)
…
Visibility between threads
…
read(V)
… Thread 1
Thread 2 V -‐ volaCle
…
write(V)
…
Visibility between threads
…
read(V)
… Thread 1
Thread 2
Same variable!
V -‐ volaCle
Happens-‐before ordering
A release and a matching later acquire establish a happens-‐before relaConship
• Program order rule. Each acCon in a thread happens-‐before every acCon in that thread that comes later in the program order. (JLS 17.4.3)
• Monitor lock rule. An unlock on a monitor lock happens-‐before every subsequent lock on that same monitor lock. (JLS 17.4.4)
• Vola9le variable rule. A write to a volaCle field happens-‐before every subsequent read of that same field. (JLS 17.4.5)
• Thread start rule. A call to Thread.start on a thread happens-‐before any other thread detects that thread has terminated, either by successfully return from Thread.join or by Thread.isAlive returning false.
• Interrup9on rule. A thread calling interrupt on another thread happens-‐before the interrupted thread detects the interrupt (either by having InterruptedExcepCon thrown, or invoking isInterrupted() or interrupted()). (JLS 17.2.3)
• Finalizer rule. The end of a constructor for an object happens-‐before the start of the finalizer for that object. (JLS 17.4.5)
• Transi9vity. If A happens-‐before B, and B happens-‐before C, then A happens-‐before C. (JLS 17.4.5)
Data race
• If there are two accesses to memory locaCon, – At least one of those is a write, and – The memory locaCon is not vola5le, then
The access must be ordered by happens-‐before
int a = o.field1; synchronized (o) { int b = o.field1; int c = o.field2; o.field3 = b + c; } doStuff();
// Block un9l obtain lock // get main memory values
// commit field3 to main memory // release lock
int a = o.field1; synchronized (o) { int b = o.field1; int c = o.field2; o.field3 = b + c; } doStuff();
// Block un9l obtain lock // get main memory values
// commit field3 to main memory // release lock
• Lock coarsening: it is allowed to take accesses outside from the lock region and move ‘em into the lock region
• a.k.a roach motel ordering: can move accesses in but cannot move ‘em out
Agenda
• Why JMM? • JMM properCes – Atomicity – Visibility – Reordering
• Vola9le • Final fields
VolaCle fields
• If a field could be accessed by mulCple threads, and at least one of those is a write, then: – Use locking to prevent simultaneous access, or – Make the field vola9le
volaCle boolean stop;
What does volaCle do?
• Reads & writes go directly to memory – Caching disabled
• VolaCle longs & doubles are atomic • VolaCles reads/writes cannot be reordered • Reads/writes become acquire/release pairs
hVp://www.infoq.com/presentaCons/Do-‐You-‐Really-‐Get-‐Memory Jevgeni Kabanov. Do You Really Get Memory
Visibility: thread terminaCon using volaCle field
private volaCle boolean stop = false;
public void run() { while(!stop){ // do stuff … } }
public void stop(){ stop = true; }
stop must be declared volaCle, otherwise, compiler could keep in register
Ordering: transmixng data via volaCle field
private vola9le boolean ready; private Object data; public Object get() { if(!ready) return null; return data; } public synchronized void set(Object o) {
if(ready) throw…; data = o; ready = true; }
Ordering: transmixng data via volaCle field
private vola9le boolean ready; private Object data; public Object get() { if(!ready) return null; return data; } public synchronized void set(Object o) {
if(ready) throw…; data = o; ready = true; }
Without vola9le, can be
reordered!
VolaCle arrays?
• volaCle A[] array; • volaCle – not transiCve: – … = array; // volaCle read – array = … // volaCle write – array[ i ] =… // normal write
• java.uCl.concurrent: – AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
Agenda
• Why JMM? • JMM properCes – Atomicity – Visibility – Reordering
• VolaCle • Final fields
Special semanCcs of final fields
class A { final B ref; public A (…) { this.ref = … ; } }
Once constructor completes, any thread can read values wriVen to the final field -‐ and the whole object tree
starCng from the field
freeze
Special semanCcs of final fields
class A { final B ref; public A (…) { this.ref = … ; } }
This should not escape the constructor: e.g. new Listener(this)
freeze
One more Cme!
Java Memory Model
• Variables: fields
• OperaCons: – R/W of instance fields (read/write) – R/W of volaCle fields (volaCle read/write) – SynchronizaCon (lock/unlock)
Java Memory Model
Atomicity
Visibility
Reordering
Atomicity
• Read/write operaCons are atomic • No out of thin-‐air values: – Any variable read operaCon should return either a default value, or the value that was assigned to this variable (somewhere else)
Atomicity
• ExcepCon: – It is allowed that reads/writes of long/double type is not atomic, but..
– … read/write of vola9le long/double must be atomic
Atomicity
• A common mistake: – For volaCle long/double only the reads and writes are atomic
– foo++, foo-‐-‐ are not atomic!
• SoluCon: – synchronized – java.u5l.concurrent.atomic
Visibility
• Again, the happens-‐before relaCon: – If X happens-‐before Y, then operaCon X is executed before and Y will will see the result of X
Visibility guarantees
• Changes made in one thread are guaranteed to be visible to other threads under following condiCons: – Explicit synchronizaCon – Thread start and terminaCon – Read/write of volaCles – First read of finals
• Thus, visibility is an issue in case if the access is not synchronized
Ordering
• Within a thread – Program order, as-‐if-‐sequenCal execuCon – Reordering is possible as long as currently execuCng thread cannot tell the difference (data dependencies)
Ordering Guarantees
• Ordering of synchronized blocks is preserved – AcCons in one synchronized block happen before thread acquires the same lock
• Ordering of read/write of volaCle fields is preserved – Effect of last write to volaCle is visible to all subsequent reads of the same volaCle
• Ordering of iniCalizaCon/access of final fields is preserved – All threads will see the correct values of final fields that where set by the constructor
x = y = 0
r1 = x
y = 1
r2 = y
x = r2
Thread 1 Thread 2
How can the result be r1 == r2 == 1 ?
start threads
The result is possible if T1’s acCons are reordered
x = y = 0
y = 1
r1 = x
r2 = y
x = r2
Thread 1 Thread 2 start threads
y = 1 r2 = y x = r2 r1 = x
r1 == r2 == 1
References / Reading • JLS, Chapter 17:
hVp://docs.oracle.com/javase/specs/jls/se7/html/jls-‐17.html • Links: hVp://www.cs.umd.edu/~pugh/java/memoryModel/ • JavaZone video: hVps://vimeo.com/28763316
• Ulrich Drepper. What Every Programmer Should Know About Memory
• D. Aspinall, J. Sevcik. Java Memory Model Examples: Good, Bad and Ugly
• J. Mason, W. Pugh, S. V. Adve. Java Memory Model