Introduced the Peterson Lock:
class Peterson implements Lock {
private boolean[] flag = new boolean[2];
private int victim;
public void lock () {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) {
// wait
}
}
public void unlock () {
int i = ThreadID.get();
flag[i] = false;
}
}
Claims:
Peterson
satisfies mutual exclusionPeterson
satisfies starvation-freedom
Suppose not…
flag[A] = true
victim = A
flag[B]
victim
flag[B] = true
victim = B
flag[A]
victim
Suppose \((B.2) \to (A.2)\):
i.e., \(A\) wrote to victim
last
if not, continue argument with roles of \(A\) and \(B\) reversed
The Peterson lock satisfies mutual exclusion
Suppose not:
lock()
method public void lock () {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) {
// wait
}
}
while
loop\(A\) stuck in:
while (flag[B] && victim == A) {
// wait
}
flag[B] = false
lock()
again, sets victim = B
So
lock()
calllock()
callflag[B] && victim == A
is true (\(A\) in while
loop)flag[A] && victim == B
is true (\(B\) in while
loop)victim
cannot be both A
and B
Nice!
Questions:
class Peterson implements Lock {
private boolean[] flag = new boolean[2];
private int victim;
public void lock () {
int i = ThreadID.get(); // get my ID, 0 or 1
int j = 1 - i; // other thread's ID
flag[i] = true; // set my flag
victim = i; // set myself to be victim
while (flag[j] && victim == i) {
// wait
}
}
public void unlock () {
int i = ThreadID.get();
flag[i] = false;
}
}
flag
variable signals intent to enter CS
victim
variable signals priority to enter CS
victim = me
means you
have priorityAfter signalling intent to enter CS
i
sets label[i]
to ticket valueTwo processes may see the same set of tickets and take same label:
label[i] == label[j]
for i != j
Solution:
Break ties by ID:
label[i] == label[j]
and i < j
, then i
has priorityUse lexicographic order on pairs (label[i], i)
Is this process fair?
0
…Fields:
boolean[] flag
flag[i] == true
indicates i
would like enter CSint[] label
label[i]
indicates “ticket” number held by i
Initialization:
flag[i] = false
, label[i] = 0
Locking Method:
public void lock () {
int i = ThreadID.get();
flag[i] = true;
label[i] = max(label[0], ..., label[n-1]) + 1;
while (!hasPriority(i)) {} // wait
}
The method hasPriority(i)
returns true
if and only if there is no k
such that
flag[k] == true
andlabel[k] < label[i]
or label[k] == label[i]
and k < i
Just lower your flag:
public void unlock() {
flag[ThreadID.get()] = false;
}
public void lock () {
int i = ThreadID.get();
flag[i] = true;
label[i] = max(label[0], ..., label[n-1]) + 1;
while (!hasPriority(i)) {} // wait
}
Why?
label
before $B$ calls lock()
,public void lock () {
int i = ThreadID.get();
flag[i] = true;
label[i] = max(label[0], ..., label[n-1]) + 1;
while (!hasPriority(i)) {} // wait
}
Why?
Thread i
calls lock()
:
i
writes label[i]
lock()
by j != i
have lower priorityk
ahead of i
eventaully releases lockSo:
i
eventually servedpublic void lock () {
int i = ThreadID.get();
flag[i] = true;
label[i] = max(label[0], ..., label[n-1]) + 1;
while (!hasPriority(i)) {} // wait
}
Suppose not:
Since $B$ entered CS:
Former can not happen: labels strictly increasing
Lamport’s Bakery Algorithm:
Is the bakery algorithm practical?
label
array contains $n$ indiceslabel
We cannot do better:
If $n$ threads want to achieve mutual exclusion + deadlock-freedom, must have $n$ read/write registers (variables)
lock()
requires 1,000s of readshasPriority
requires either 1,000s of reads or a more advanced data structureArgument relies crucially on fact that the only atomic operations are read
and write
Modern computers offer more powerful atomic operations
AtomicBoolean
class
getAndSet()
compareAndSet()
Concurrent Objects!