Added SynchronizedComparison.java
This commit is contained in:
parent
bd6b0af400
commit
5710360504
@ -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
|
||||
*/
|
77
lowlevel/SynchronizedComparison.java
Normal file
77
lowlevel/SynchronizedComparison.java
Normal 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
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user