}
public PairO { this(0, 0); } public int getXO { return x; } public int getYO { return y; } public void incrementXO { x++; } public void incrementYO { y++; } public String toStringO {
return "x; " + x + ", y; " + y;
}
public class PairValuesNotEqualException extends RuntimeException {
public Pai rValuesNotEqual Excepti onO {
superC'Pair values not equal; " + Pair.this);
}
}
// Произвольный инвариант - обе переменные должны быть равны; public void checkStateO { if(x != у)
throw new PairValuesNotEqualException();
}
}
// Защита класса Pair внутри приспособленного к потокам класса; abstract class PairManager {
Atomiclnteger checkCounter = new AtomicInteger(O). protected Pair p = new PairO. private List
Collections synchronizedList(new ArrayList
// Создаем копию, чтобы сохранить оригинал в безопасност return new Pair(p getXO, p getYO).
}
// Предполагается, что операция занимает некоторое время protected void store(Pair р) { storage add(p), try {
TimeUnit MILLISECONDS sleep(50); } catch(InterruptedException ignore) {}
}
public abstract void incrementO.
}
// Синхронизация всего метода.
class PairManagerl extends PairManager { public synchronized void incrementO { p.incrementXO. p incrementYO. store(getPairO).
// Использование критической секции-class PairManager2 extends PairManager { public void incrementO { Pair temp.
synchronized(this) {
p incrementXO; p. incrementYO; temp = getPairO,
}
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 toStringO {
return "Pair: " + pm.getPairO +
" checkCounter = " + pm checkCounter.get О;
}
}
class PairChecker implements Runnable { private PairManager pm; public PairChecker(PairManager pm) {
}
public class CriticalSection { // Сравнение двух подходов-static void
testApproaches(PairManager pmanl. PairManager pman2) {
ExecutorService exec = Executors newCachedThreadPool(). PairManipulator
pml = new PairManipulator(pmanl), pm2 = new PairManipulator(pman2), PairChecker
pcheckl = new PairChecker(pmanl), pcheck2 = new PairChecker(pman2), exec execute(pml), exec execute(pm2), exec execute(pcheckl); exec execute(pcheck2), try {
TimeUnit MILLISECONDS sleep(500); } catchdnterruptedException e) {
System out.printlnC'Sleep interrupted"),
}
System.out printin("pml " + pml + "\npm2: " + pm2). System exit(O),
}
public static void main(String[] args) { PairManager
pmanl = new PairManagerlO, pman2 = new PairManager2(); testApproaches(pmanl. pman2);
}
} /* Output-
pml- Pair. x. 15, у 15 checkCounter = 272565 pm2- Pair- x. 16, y: 16 checkCounter = 3956974 */// ~
Как было отмечено, класс Pair не приспособлен к работе с потоками, поскольку его инвариант (предположительно произвольный) требует равенства обоих переменных. Вдобавок, как мы уже видели в этой главе, операции инкремента небезопасны в отношении к потокам, и, так как ни один из методов не был объявлен как synchronized, мы не можем считать, что объект Pair останется неповрежденным в многопоточной программе.