Cześć :)
Popatrzcie na kod:
package concurrency;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;
class Pair { // Niezabezpieczona
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++; }
public String toString() {
return "x: " + x + ", y: " + y;
}
public class PairValuesNotEqualException
extends RuntimeException {
public PairValuesNotEqualException() {
super("Wartoci pary s¹ nierówne: " + Pair.this);
}
}
// Niezmiennik -- obie zmienne musz¹ byæ równe:
public void checkState() {
if(x != y)
throw new PairValuesNotEqualException();
}
}
// Ochrona klasy Pair w klasie zabezpieczonej na
// okolicznoæ pracy wielow¹tkowej:
abstract class PairManager {
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
private List<Pair> storage =
Collections.synchronizedList(new ArrayList<Pair>());
public synchronized Pair getPair() {
// Utworzenie kopii w celu zabezpieczenia orygina³u:
return new Pair(p.getX(), p.getY());
}
// Zak³adamy, ¿e to d³ugotrwa³a operacja
protected void store(Pair p) {
storage.add(p);
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch(InterruptedException ignore) {}
}
public abstract void increment();
}
// Synchronizacja ca³ej metody:
class PairManager1 extends PairManager {
public synchronized void increment() {
p.incrementX();
p.incrementY();
store(getPair());
}
}
// Zastosowanie sekcji krytycznej:
class PairManager2 extends PairManager {
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;
}
public void run() {
while(true){
pm.increment();
}
}
public String toString() {
return "Para: " + pm.getPair() +
" licznik = " + pm.checkCounter.get();
}
}
class PairChecker implements Runnable {
private PairManager pm;
public PairChecker(PairManager pm) {
this.pm = pm;
}
public void run() {
while(true) {
pm.checkCounter.incrementAndGet();
pm.getPair().checkState();
}
}
}
public class CriticalSection {
// test obu rozwi¹zañ:
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(100);
} catch(InterruptedException e) {
System.out.println("Uspienie przerwane");
}
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: Para: x: 15, y: 15 licznik = 272565
pm2: Para: x: 16, y: 16 licznik = 3956974
*///:~
Dlaczego ten program nie daje żadnych reultatów na ekran?
Co mnie jednak najbardziej dziwi?
To, że zmiana metody run() w PairManipulator na:
public void run() {
while(true){
System.out.println("c");
pm.increment();
}
}
( dodana tylko linijka wypisywania sprawi, że nastąpi przerwanie i rezultat na ekranie:
pm1: Para: x: 2, y: 2 licznik = 1110
pm2: Para: x: 3, y: 3 licznik = 3835311
Dlaczego tak się dzieje?