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 AtomicInteger
s 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!