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?
Why?
Thread i calls lock():
i writes label[i]
lock() by j != i have lower priorityk ahead of i eventually 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:
Why can’t 1 happen?
Lamport’s Bakery Algorithm:
Two Issues:
hasPriority method is costly0, 1,...
long valuesHomework 2 will have questions that address these issues.
We cannot do better!
Consider $n$ threads, $m < n$ shared memory locations
A covering state is a step in an execution in which:
write operationIf an execution reaches a covering state, then the protocol does not satisfy mutual exclusion.
Why?
Show. Any protocol with $m < n$ memory locations attains a covering state in some execution.
Consequences:
read/write then $n$ shared memory locations are necessary for deadlock-free mutual exclusion with $n$ threads
Argument relies crucially on fact that the only atomic operations are read and write
Modern computers offer more powerful atomic operations
In Java, AtomicInteger class
getAndIncrement() is supported atomic operationHomework 2 Use AtomicIntegers to get a cleaner and more efficient realization of Lamport’s bakery idea.

In Java, int and float values are 32 bits long
In modern CPUs, registers are larger
int a = 573842;
int b = 3847253;
int c = a + b;
int a1 = 573842;
int b1 = 3847253;
int c1 = a1 + b1;
int a2 = 38657548;
int b2 = 438573;
int c2 = a2 + b2;
int[] a = new int[n];
int[] b = new int[n];
int[] c = new int[n];
for (int i = 0; i < n; i++) {
c[i] = a[i] + b[i];
}
Suppose we can load step values into each register
int[] a = new int[n];
int[] b = new int[n];
int[] c = new int[n];
for (int i = 0; i < n; i += step) {
c[i] = a[i] + b[i];
c[i+1] = a[i+1] + b[i+1];
...
c[i+step-1] = a[i+step-1] + b[i+step-1]
}
Allows us to specify Vector objects
Vector is like fixed-size arrayVector (bit) size to same as hardware registersNotes:
Vector)Find entry-wise minimum of arrays:
VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
...
public static float[] vectorMax(float[] a, float[] b) {
float[] c = new float[a.length];
int step = SPECIES.length();
int bound = SPECIES.loopBound(a.length);
...
}
Find entry-wise minimum of arrays:
...
int i = 0;
for (; i < bound; i += step) {
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.max(vb);
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = Math.max(a[i], b[i]);
}
return c;
}
The FloatVector has 8 lanes.
Computing max array with simple methods...
That took 927 ms.
Computing max array with vector methods...
That took 572 ms.
The arrays are equal!
Use Vector operations to speed up programs!