Atomic Variable and Non Blocking Synchronization - Programming Language | CMSC 433, Exams of Programming Languages

Material Type: Exam; Class: PROG LANG TECH & PDGMS; Subject: Computer Science; University: University of Maryland; Term: Spring 2009;

Typology: Exams

Pre 2010

Uploaded on 07/29/2009

koofers-user-pyt
koofers-user-pyt 🇺🇸

10 documents

1 / 16

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
4/16/09'
1'
Atomic Variables
&
Nonblocking Synchronization
A Locking Counter
public final class Counter {
private long value = 0;
public synchronized long getValue() {
return value;
}
public synchronized long increment() {
return ++value;
}
}
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff

Partial preview of the text

Download Atomic Variable and Non Blocking Synchronization - Programming Language | CMSC 433 and more Exams Programming Languages in PDF only on Docsity!

Atomic Variables

Nonblocking Synchronization

A Locking Counter

public final class Counter {

private long value = 0;

public synchronized long getValue() {

return value;

public synchronized long increment() {

return ++value;

Java.util.concurrent Performance

• Many java.util.concurrent classes perform better

then synchronized alternatives. Why?

– Atomic variables & nonblocking synchronization

• We’ve already talked about atomic variables.

• Nonblocking algorithms are concurrent algorithms

that derive their thread safety from low-level

atomic hardware primitives (not locks).

Disadvantages of Locking

• When a thread fails to acquire lock it can be

suspended

  • Context switching & resumption can be expensive

• When waiting for a lock thread can’t do anything

• If thread holding lock is delayed, no thread that needs

that lock can progress

  • Priority inversion: low priority thread has lock needed by a high priority thread

• Caveat: contention, rather than locking, is the real

issue. YMMV

Simulated (CAS)

public class SimulatedCAS { // not implemented this way! private int currValue; public synchronized int get() {return currValue;} public synchronized int compareAndSwap ( int expectedValue, int newValue) { if (currValue == expectedValue) currValue = newValue; return currValue ; } public synchronized boolean compareAndSet(int expectedValue, int newValue) { return (expectedValue == compareAndSwap(expectedValue, newValue)); } }

A Nonblocking Counter

public class NonblockingCounter { private AtomicInteger value; public int getValue() { return value.get(); } public int increment() { int v; do { v = value.get(); }while (!value.compareAndSet(v, v + 1)); return v + 1; } }

Atomic Variables

• Generalization of volatile variables

• Allows atomic read-modify-write operations

without intrinsic locking

• Scope of contention limited to a single variable

• Faster than locking -- no scheduling impact

• Like volatiles, can’t synchronize two atomic vars

• Doesn’t support atomic check-then-act sequences

Updating Complex Objects

• Example: Want to manage two related variables

• Can’t do that with volatiles

• Idiom: turn compound update into single update

Performance Comparison

• Will show two implementations of a psuedo-

random number generator (PRNG)

– One uses locks: ReentrantLockPseudoRandom.java

– One is nonblocking: AtomicPseudoRandom.java

• PRNG issues

– Next value based on last value, so you need to

remember last value

• How do lock-based and non-lock-based

implementations compare?

ReentrantLockPseudoRandom

public class ReentrantLockPseudoRandom extends PseudoRandom { private final Lock lock = new ReentrantLock(false); private int seed; ReentrantLockPseudoRandom(int seed) {this.seed = seed;} public int nextInt(int n) { lock.lock(); try { int s = seed; seed = calculateNext(s); int remainder = s % n; return remainder > 0? remainder : remainder + n; } finally { lock.unlock();} } }

AtomicPseudoRandom

public class AtomicPseudoRandom extends PseudoRandom { private AtomicInteger seed; AtomicPseudoRandom(int seed) {this.seed = new AtomicInteger(seed);} public int nextInt(int n) { while (true) { int s = seed.get(); int nextSeed = calculateNext(s); if (seed.compareAndSet(s, nextSeed)) { int remainder = s % n; return remainder > 0? remainder : remainder + n; } } } }

Atomic Updates / Lock Updates

0 1 2 3 4 5 6 7 100 50 25 10 5 #Threads

Nonblocking Stack

• See: ConcurrentStack.java & SynchStack.java

Nonblocking Stack

public class ConcurrentStack { private static class Node { public final E item; public Node next; public Node(E item) { this.item = item; } } AtomicReference<Node> top = new AtomicReference<Node>(); public void push(E item) { Node newHead = new Node(item); Node oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); }

Nonblocking Stack

public E pop() { Node oldHead; Node newHead; do { oldHead = top.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!top.compareAndSet(oldHead, newHead)); return oldHead.item; } }

A Nonblocking Queue

• Rule of thumb– limit change to one variable

• Harder for a Queue because we need to update

head and tail

• See:

Michael & Scott Nonblocking Queue

• Queue with two elements in quiescent state

Michael & Scott Nonblocking Queue

• Queue in intermediate state during insertion

– After the new element is added but before the tail

pointer is updated

Michael & Scott Nonblocking Queue

• Queue in quiescent state again after the tail pointer

is updated

Michael & Scott Nonblocking Queue

• Observation: if tail.next is non-null, then a

operation is in progress

• If a thread finds an operation in progress, it will

try to advance tail to return queue to stable state

– Then it will reload tail and repeat process

ConcurrentQueue

public E take() { for (;;) { // # Keep trying until take is done Node oldHead = head.get(); // get current head Node oldTail = tail.get(); // get current tail Node oldHeadNext = oldHead.next.get(); // get current head.next if (oldHead == head.get()) { // Are head, tail, and next consistent? if (oldHead == oldTail) { // Queue empty or tail being updated? if (oldHeadNext == null) // Is queue empty? return null; // Queue is empty, can't take tail.compareAndSet(oldTail, oldHeadNext); // tail updated. try to advance it } else { // No need to deal with tail if (head.compareAndSet(oldHead, oldHeadNext)) return oldHeadNext.item; } } }