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) {};
}
public void unlock () {
int i = ThreadID.get();
flag[i] = false;
}
}
Peterson lock assumes 2 threads, with IDs 0 and 1
Manually set an ID for threads
public class PetersonThread extends Thread {
private int id;
private LockedCounter ctr;
private int numIncrements;
public PetersonThread (int id, LockedCounter ctr, int numIncrements) {
super();
this.id = id;
this.ctr = ctr;
this.numIncrements = numIncrements;
}
public int getPetersonId() {
return id;
}
@Override
public void run () {
for (int i = 0; i < numIncrements; ++i) {
ctr.increment();
}
}
}
Next week: A better way
PetersonLock
class PetersonLock {
private boolean[] flag = new boolean[2];
private int victim;
public void lock () {
int i = ((PetersonThread)Thread.currentThread()).getPetersonId();
int j = 1 - i;
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock () {
int i = ((PetersonThread)Thread.currentThread()).getPetersonId();
flag[i] = false;
}
}
public class LockedCounter {
private int count = 0;
PetersonLock lock = new PetersonLock();
public void increment () {
lock.lock();
try {
++count;
} finally {
lock.unlock();
}
}
public int read () {
return count;
}
}
What happened?

volatile VariablesJava can make variables visible between threads:
volatile keywordvolatile are atomicDrawbacks:
volatile variables are less efficientcount++ not atomicvolatile SomeClass..., only the reference is treated as volatilevolatile?PetersonLock?LockedCounter?PetersonLock Againclass PetersonLock {
private boolean[] flag = new boolean[2];
private int victim;
public void lock () {
int i = ((PetersonThread)Thread.currentThread()).getPetersonId();
int j = 1 - i;
flag[i] = true;
victim = i;
while (flag[j] && victim == i) {};
}
public void unlock () {
int i = ((PetersonThread)Thread.currentThread()).getPetersonId();
flag[i] = false;
}
}
Only primitive datatypes can be volatile
volatile boolean[] flag makes the reference volatile, not the data itselfHow to fix this?
Just make 2 boolean variables, flag0 and flag1
LockedCounter Againpublic class LockedCounter {
private int count = 0;
PetersonLock lock = new PetersonLock();
public void increment () {
lock.lock();
try {
++count;
} finally {
lock.unlock();
}
}
public int read () {
return count;
}
}
What have we done?
Theory and practice converge!
PetersonLock
volatile variables
n registers (variables) for lock with n threadsBetter locks through atomics:
AtomicBoolean supports atomic operationsab.getAndSet(boolean newValue)ab’s value to newValue and returns previous valueHow could you use a single AtomicBoolean to implement a lock?
public class TASLock {
AtomicBoolean isLocked = new AtomicBoolean(false);
public void lock () {
while (isLocked.getAndSet(true)) {}
}
public void unlock () {
isLocked.set(false);
}
}
public class TASLock {
AtomicBoolean isLocked = new AtomicBoolean(false);
public void lock () {
while (isLocked.getAndSet(true)) {}
}
public void unlock () {
isLocked.set(false);
}
}
TASLock Perform?TTASLock
In TASLock, we do a lot of getAndSet() operations under contention
public void lock () {
while (isLocked.getAndSet(true)) {}
}
Since getAndSet is expensive, we could try:
getAndSet if we’ve seen isLocked.get() == false
public void lock () {
while (true) {
while (isLocked.get()) {};
if (!isLocked.getAndSet(true)) {
return;
}
}
}
How does this affect performance?