1. Clearpoint Techdrop
Concurrency
@mikeb2701
http://bad-concurrency.blogspot.com
Friday, 8 March 13
2. public class OrderController {
private Counter orderCounter; // What implementation???
public void placeOrder(long productId,
long customerId) {
orderCounter.increment();
// Place the order
}
}
Friday, 8 March 13
3. public class BrokenCounter implements Counter {
private long value = 0;
@Override
public void increment() {
value++;
}
@Override
public long getValue() {
return value;
}
}
Friday, 8 March 13
4. public class SynchronizedCounter implements Counter {
private final Object mutex = new Object();
private long value = 0;
public void increment() {
synchronized (mutex) {
value++;
}
}
public long getValue() {
return value;
}
}
Friday, 8 March 13
5. public class LockedCounter implements Counter {
private final Lock l = new ReentrantLock();
private long value = 0;
public void increment() {
l.lock();
try {
value++;
} finally {
l.unlock();
}
}
public long getValue() {
return value;
}
}
Friday, 8 March 13
7. public class AtomicCounter implements Counter {
private final AtomicLong value = new AtomicLong(0);
@Override
public void increment() {
value.incrementAndGet();
}
@Override
public long getValue() {
return value.get();
}
}
Friday, 8 March 13
8. public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
Friday, 8 March 13
10. public class ThreadLocalCounter implements Counter {
ConcurrentLinkedQueue<AtomicLong> values =
new ConcurrentLinkedQueue<AtomicLong>();
private final ThreadLocal<AtomicLong> counterLocal =
new ThreadLocal<AtomicLong>() {
protected AtomicLong initialValue() {
AtomicLong value = new AtomicLong();
values.add(value);
return value;
}
};
public void increment() {
AtomicLong atomicLong = counterLocal.get();
atomicLong.lazySet(atomicLong.get() + 1);
}
public long getValue() {
long l = 0;
for (AtomicLong value : values) {
l += value.get();
}
return l;
}
}
Friday, 8 March 13
17. Causality
Causality
Fear will keep the
local systems inline.
instructions
- Grand Moff Wilhuff Tarkin
Friday, 8 March 13
18. 1st
2nd Operation
Operation
Normal Load Volatile Load Volatile Store
Normal Store Monitor Enter Monitor Exit
Normal Load
Normal Store NO
Volatile Load
Monitor Enter NO NO NO
Volatile Store
Monitor Exit NO NO
Friday, 8 March 13
19. • Loads are not reordered with other loads.
• Stores are not reordered with other stores.
• Stores are not reordered with older loads.
• In a multiprocessor system, memory ordering obeys causality (memory
ordering respects transitive visibility).
• In a multiprocessor system, stores to the same location have a total order.
• In a multiprocessor system, locked instructions to the same location have a
total order.
• Loads and Stores are not reordered with locked instructions.
Friday, 8 March 13
22. public class AtomicLong extends Number
implements Serializable {
// ...
private volatile long value;
// ...
/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(long newValue) {
value = newValue;
}
// ...
}
Friday, 8 March 13
23. # {method} 'set' '(J)V' in 'java/util/concurrent/atomic/AtomicLong'
# this: rsi:rsi = 'java/util/concurrent/atomic/AtomicLong'
# parm0: rdx:rdx = long
# [sp+0x20] (sp of caller)
mov 0x8(%rsi),%r10d
shl $0x3,%r10
cmp %r10,%rax
jne 0x00007f1f410378a0 ; {runtime_call}
xchg %ax,%ax
nopl 0x0(%rax,%rax,1)
xchg %ax,%ax
push %rbp
sub $0x10,%rsp
nop
mov %rdx,0x10(%rsi)
lock addl $0x0,(%rsp) ;*putfield value
; - j.u.c.a.AtomicLong::set@2 (line 112)
add $0x10,%rsp
pop %rbp
test %eax,0xa40fd06(%rip) # 0x00007f1f4b471000
; {poll_return}
Friday, 8 March 13
24. public class AtomicLong extends Number
implements Serializable {
// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
// ...
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(long newValue) {
unsafe.putOrderedLong(this, valueOffset, newValue);
}
// ...
}
Friday, 8 March 13
25. # {method} 'lazySet' '(J)V' in 'java/util/concurrent/atomic/
AtomicLong'
# this: rsi:rsi = 'java/util/concurrent/atomic/AtomicLong'
# parm0: rdx:rdx = long
# [sp+0x20] (sp of caller)
mov 0x8(%rsi),%r10d
shl $0x3,%r10
cmp %r10,%rax
jne 0x00007f1f410378a0 ; {runtime_call}
xchg %ax,%ax
nopl 0x0(%rax,%rax,1)
xchg %ax,%ax
push %rbp
sub $0x10,%rsp
nop
mov %rdx,0x10(%rsi) ;*invokevirtual putOrderedLong
; - AtomicLong::lazySet@8 (line 122)
add $0x10,%rsp
pop %rbp
test %eax,0xa41204b(%rip) # 0x00007f1f4b471000
; {poll_return}
Friday, 8 March 13
26. public class AtomicInteger extends Number
implements Serializable {
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;
//...
public final boolean compareAndSet(int expect,
int update) {
return unsafe.compareAndSwapInt(this, valueOffset,
expect, update);
}
}
Friday, 8 March 13
27. # {method} 'compareAndSet' '(JJ)Z' in 'java/util/concurrent/
atomic/AtomicLong'
# this: rsi:rsi = 'java/util/concurrent/atomic/AtomicLong'
# parm0: rdx:rdx = long
# parm1: rcx:rcx = long
# [sp+0x20] (sp of caller)
mov 0x8(%rsi),%r10d
shl $0x3,%r10
cmp %r10,%rax
jne 0x00007f6699037a60 ; {runtime_call}
xchg %ax,%ax
nopl 0x0(%rax,%rax,1)
xchg %ax,%ax
sub $0x18,%rsp
mov %rbp,0x10(%rsp)
mov %rdx,%rax
lock cmpxchg %rcx,0x10(%rsi)
sete %r11b
movzbl %r11b,%r11d ;*invokevirtual compareAndSwapLong
; - j.u.c.a.AtomicLong::compareAndSet@9 (line
149)
mov %r11d,%eax
add $0x10,%rsp
pop %rbp
test %eax,0x91df935(%rip) # 0x00007f66a223e000
; {poll_return}
Friday, 8 March 13
29. Example - Disruptor Multi-producer
private void publish(Disruptor disruptor, long value) {
long next = disruptor.next();
disruptor.setValue(next, value);
disruptor.publish(next);
}
Friday, 8 March 13
30. Example - Disruptor Multi-producer
public long next() {
long next;
long current;
do {
current = nextSequence.get();
next = current + 1;
while (next > (readSequence.get() + size)) {
LockSupport.parkNanos(1L);
continue;
}
} while (!nextSequence.compareAndSet(current, next));
return next;
}
Friday, 8 March 13
31. Algorithm: Spin - 1
public void publish(long sequence) {
long sequenceMinusOne = sequence - 1;
while (cursor.get() != sequenceMinusOne) {
// Spin
}
cursor.lazySet(sequence);
}
Friday, 8 March 13
35. Algorithm: Buffer
public long next() {
long next;
long current;
do {
current = cursor.get();
next = current + 1;
while (next > (readSequence.get() + size)) {
LockSupport.parkNanos(1L);
continue;
}
} while (!cursor.compareAndSet(current, next));
return next;
}
Friday, 8 March 13
36. Algorithm: Buffer
public void publish(long sequence) {
int publishedValue = (int) (sequence >>> indexShift);
published.set(indexOf(sequence), publishedValue);
}
// Get Value
int availableValue = (int) (current >>> indexShift);
int index = indexOf(current);
while (published.get(index) != availableValue) {
// Spin
}
Friday, 8 March 13