2015-04-20 15:36:01 -07:00
|
|
|
|
//: concurrency/CriticalSection.java
|
2015-05-29 14:18:51 -07:00
|
|
|
|
// <20>2015 MindView LLC: see Copyright.txt
|
2015-05-05 11:20:13 -07:00
|
|
|
|
// {TimeOutDuringTesting}
|
|
|
|
|
// (Behavior may have changed in Java 8).
|
2015-04-20 15:36:01 -07:00
|
|
|
|
// Synchronizing blocks instead of entire methods. Also
|
|
|
|
|
// demonstrates protection of a non-thread-safe class
|
|
|
|
|
// with a thread-safe one.
|
|
|
|
|
package concurrency;
|
|
|
|
|
import java.util.concurrent.*;
|
|
|
|
|
import java.util.concurrent.atomic.*;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
class Pair { // Not thread-safe
|
|
|
|
|
private int x, y;
|
|
|
|
|
public Pair(int x, int y) {
|
|
|
|
|
this.x = x;
|
|
|
|
|
this.y = y;
|
|
|
|
|
}
|
|
|
|
|
public Pair() { this(0, 0); }
|
|
|
|
|
public int getX() { return x; }
|
|
|
|
|
public int getY() { return y; }
|
|
|
|
|
public void incrementX() { x++; }
|
|
|
|
|
public void incrementY() { y++; }
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public String toString() {
|
|
|
|
|
return "x: " + x + ", y: " + y;
|
|
|
|
|
}
|
|
|
|
|
public class PairValuesNotEqualException
|
|
|
|
|
extends RuntimeException {
|
|
|
|
|
public PairValuesNotEqualException() {
|
|
|
|
|
super("Pair values not equal: " + Pair.this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Arbitrary invariant -- both variables must be equal:
|
|
|
|
|
public void checkState() {
|
|
|
|
|
if(x != y)
|
|
|
|
|
throw new PairValuesNotEqualException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Protect a Pair inside a thread-safe class:
|
|
|
|
|
abstract class PairManager {
|
|
|
|
|
AtomicInteger checkCounter = new AtomicInteger(0);
|
|
|
|
|
protected Pair p = new Pair();
|
|
|
|
|
private List<Pair> storage =
|
2015-05-05 11:20:13 -07:00
|
|
|
|
Collections.synchronizedList(new ArrayList<>());
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public synchronized Pair getPair() {
|
|
|
|
|
// Make a copy to keep the original safe:
|
|
|
|
|
return new Pair(p.getX(), p.getY());
|
|
|
|
|
}
|
|
|
|
|
// Assume this is a time consuming operation
|
|
|
|
|
protected void store(Pair p) {
|
|
|
|
|
storage.add(p);
|
|
|
|
|
try {
|
|
|
|
|
TimeUnit.MILLISECONDS.sleep(50);
|
|
|
|
|
} catch(InterruptedException ignore) {}
|
|
|
|
|
}
|
|
|
|
|
public abstract void increment();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Synchronize the entire method:
|
|
|
|
|
class PairManager1 extends PairManager {
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public synchronized void increment() {
|
|
|
|
|
p.incrementX();
|
|
|
|
|
p.incrementY();
|
|
|
|
|
store(getPair());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use a critical section:
|
|
|
|
|
class PairManager2 extends PairManager {
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public void increment() {
|
|
|
|
|
Pair temp;
|
|
|
|
|
synchronized(this) {
|
|
|
|
|
p.incrementX();
|
|
|
|
|
p.incrementY();
|
|
|
|
|
temp = getPair();
|
|
|
|
|
}
|
|
|
|
|
store(temp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PairManipulator implements Runnable {
|
|
|
|
|
private PairManager pm;
|
|
|
|
|
public PairManipulator(PairManager pm) {
|
|
|
|
|
this.pm = pm;
|
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public void run() {
|
|
|
|
|
while(true)
|
|
|
|
|
pm.increment();
|
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public String toString() {
|
|
|
|
|
return "Pair: " + pm.getPair() +
|
|
|
|
|
" checkCounter = " + pm.checkCounter.get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PairChecker implements Runnable {
|
|
|
|
|
private PairManager pm;
|
|
|
|
|
public PairChecker(PairManager pm) {
|
|
|
|
|
this.pm = pm;
|
|
|
|
|
}
|
2015-05-05 11:20:13 -07:00
|
|
|
|
@Override
|
2015-04-20 15:36:01 -07:00
|
|
|
|
public void run() {
|
|
|
|
|
while(true) {
|
|
|
|
|
pm.checkCounter.incrementAndGet();
|
|
|
|
|
pm.getPair().checkState();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class CriticalSection {
|
|
|
|
|
// Test the two different approaches:
|
|
|
|
|
static void
|
|
|
|
|
testApproaches(PairManager pman1, PairManager pman2) {
|
|
|
|
|
ExecutorService exec = Executors.newCachedThreadPool();
|
|
|
|
|
PairManipulator
|
|
|
|
|
pm1 = new PairManipulator(pman1),
|
|
|
|
|
pm2 = new PairManipulator(pman2);
|
|
|
|
|
PairChecker
|
|
|
|
|
pcheck1 = new PairChecker(pman1),
|
|
|
|
|
pcheck2 = new PairChecker(pman2);
|
|
|
|
|
exec.execute(pm1);
|
|
|
|
|
exec.execute(pm2);
|
|
|
|
|
exec.execute(pcheck1);
|
|
|
|
|
exec.execute(pcheck2);
|
|
|
|
|
try {
|
|
|
|
|
TimeUnit.MILLISECONDS.sleep(500);
|
|
|
|
|
} catch(InterruptedException e) {
|
|
|
|
|
System.out.println("Sleep interrupted");
|
|
|
|
|
}
|
|
|
|
|
System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
|
|
|
|
|
System.exit(0);
|
|
|
|
|
}
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
PairManager
|
|
|
|
|
pman1 = new PairManager1(),
|
|
|
|
|
pman2 = new PairManager2();
|
|
|
|
|
testApproaches(pman1, pman2);
|
|
|
|
|
}
|
|
|
|
|
} /* Output: (Sample)
|
|
|
|
|
pm1: Pair: x: 15, y: 15 checkCounter = 272565
|
|
|
|
|
pm2: Pair: x: 16, y: 16 checkCounter = 3956974
|
2015-05-05 11:20:13 -07:00
|
|
|
|
*///:~
|