Added SynchronizedComparison.java

This commit is contained in:
Bruce Eckel 2017-01-14 00:34:41 -08:00
parent bd6b0af400
commit 5710360504
2 changed files with 77 additions and 143 deletions

View File

@ -1,143 +0,0 @@
// lowlevel/CriticalSection.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Synchronizing blocks instead of entire methods. Also
// demonstrates protection of a non-thread-safe class
// with a thread-safe one.
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;
import onjava.*;
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++; }
@Override
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 =
Collections.synchronizedList(new ArrayList<>());
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);
new Nap(50);
}
public abstract void increment();
}
// Synchronize the entire method:
class PairManager1 extends PairManager {
@Override
public synchronized void increment() {
p.incrementX();
p.incrementY();
store(getPair());
}
}
// Use a critical section:
class PairManager2 extends PairManager {
@Override
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;
}
@Override
public void run() {
while(true)
pm.increment();
}
@Override
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;
}
@Override
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 es = Executors.newCachedThreadPool();
PairManipulator
pm1 = new PairManipulator(pman1),
pm2 = new PairManipulator(pman2);
PairChecker
pcheck1 = new PairChecker(pman1),
pcheck2 = new PairChecker(pman2);
es.execute(pm1);
es.execute(pm2);
es.execute(pcheck1);
es.execute(pcheck2);
new Nap(500);
System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
System.exit(0);
}
public static void main(String[] args) {
new TimedAbort(4);
PairManager
pman1 = new PairManager1(),
pman2 = new PairManager2();
testApproaches(pman1, pman2);
}
}
/* Output:
TimedAbort 4
*/

View File

@ -0,0 +1,77 @@
// lowlevel/SynchronizedComparison.java
// (c)2017 MindView LLC: see Copyright.txt
// We make no guarantees that this code is fit for any purpose.
// Visit http://OnJava8.com for more book information.
// Synchronizing blocks instead of entire methods
// speeds up access.
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import onjava.Nap;
abstract class Guarded {
AtomicLong callCount = new AtomicLong();
public abstract void method();
@Override
public String toString() {
return getClass().getSimpleName() +
": " + callCount.get();
}
}
class SynchronizedMethod extends Guarded {
public synchronized void method() {
new Nap(10);
callCount.incrementAndGet();
}
}
class CriticalSection extends Guarded {
public void method() {
new Nap(10);
synchronized(this) {
callCount.incrementAndGet();
}
}
}
class Caller implements Runnable {
private Guarded g;
public Caller(Guarded g) { this.g = g; }
private AtomicBoolean stop =
new AtomicBoolean(false);
class Stop extends TimerTask {
@Override
public void run() { stop.set(true); }
}
@Override
public void run() {
new Timer().schedule(new Stop(), 2500);
while(!stop.get())
g.method();
}
}
public class SynchronizedComparison {
static void test(Guarded g) {
List<CompletableFuture<Void>> callers =
Stream.of(
new Caller(g),
new Caller(g),
new Caller(g),
new Caller(g))
.map(CompletableFuture::runAsync)
.collect(Collectors.toList());
callers.forEach(CompletableFuture::join);
System.out.println(g);
}
public static void main(String[] args) {
test(new CriticalSection());
test(new SynchronizedMethod());
}
}
/* Output:
CriticalSection: 972
SynchronizedMethod: 247
*/