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 priority
After 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!