我现在阅读书籍Thinking in Java,关于关键部分的章节,我无法理解一个例子,因为我得到了书中没有描述的例外。示例如下所示:
class Pair {
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 class PairValuesNotEqualException extends RuntimeException {
public PairValuesNotEqualException() {
super("Values are not equal: " + Pair.this);
}
}
public void checkState() {
if (x != y) {
throw new PairValuesNotEqualException();
}
}
}
abstract class PairManager {
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
public synchronized Pair getPair() {
// Make copies to protect the original
return new Pair(p.getX(), p.getY());
}
public abstract void increment();
}
// synchronization of the whole method
class PairManager1 extends PairManager {
@Override
public synchronized void increment() {
p.incrementX();
p.incrementY();
}
}
// Critical section
class PairManager2 extends PairManager {
@Override
public void increment() {
synchronized (this) {
p.incrementX();
p.incrementY();
}
}
}
class PairManipulator implements Runnable {
private PairManager pairManager;
public PairManipulator(PairManager pairManager) {
this.pairManager = pairManager;
}
@Override
public void run() {
while (true)
pairManager.increment();
}
}
class PairChecker implements Runnable {
private PairManager pairManager;
public PairChecker(PairManager pairManager) {
this.pairManager = pairManager;
}
@Override
public void run() {
while (true) {
pairManager.checkCounter.incrementAndGet();
pairManager.getPair().checkState();
}
}
}
public class CriticalSection {
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("InterruptedException");
}
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);
}
}
示例输出:
pm1: Pair: Pair{x=364, y=364} counter = 471421
pm2: Pair: Pair{x=365, y=365} counter = 1015604598
这个例子毫无例外地执行。
在上面的示例中,我了解它是如何工作的,但问题出在显式锁的示例中。 来自book的显式锁定示例:
class ExplicitPairManager1 extends PairManager {
private Lock lock = new ReentrantLock();
// why synchronized ??
public synchronized void increment() {
lock.lock();
try {
p.incrementX();
p.incrementY();
} finally {
lock.unlock();
}
}
}
class ExplicitPairManager2 extends PairManager {
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
p.incrementX();
p.incrementY();
} finally {
lock.unlock();
}
}
}
public class ExplicitCriticalSection {
public static void main(String[] args) throws Exception {
PairManager
pm1 = new ExplicitPairManager1(),
pm2 = new ExplicitPairManager2();
CriticalSection.testApproaches(pm1, pm2);
}
}
输出:
Exception in thread "pool-1-thread-4" critical.sections.Pair$PairValuesNotEqualException: Values are not equal: Pair{x=2, y=1}
at critical.sections.Pair.checkState(CriticalSection.java:49)
at critical.sections.PairChecker.run(CriticalSection.java:133)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pm1: Pair: Pair{x=1024, y=1024} counter = 3
pm2: Pair: Pair{x=1025, y=1025} counter = 1499445
首先,我不明白为什么作者在ExplicitPairManager1#increment中使用synchronized
,如果他也使用Lock对象?这是书中的错误吗?
第二个问题是我不明白为什么我会例外?
引入了豁免:
class PairChecker implements Runnable {
private PairManager pairManager;
public PairChecker(PairManager pairManager) {
this.pairManager = pairManager;
}
@Override
public void run() {
while (true) {
pairManager.checkCounter.incrementAndGet();
pairManager.getPair().checkState(); // here was thrown an exception
}
}
}
为什么我得到了饶恕和作者不?可能的JVM行为在不同的系统上是不同的吗?我使用Ubuntu 16.04 LTS和Java 8。
答案 0 :(得分:0)
如果要为多个线程建立临界区,则需要在同一对象上进行同步。
ExplicitPairManager2
修改了对的异常。
让我们看看异常导致流程的可能性如何:
ExplicitPairManager2.lock.lock()
ExplicitPairManager2.p.incrementX()
发生了getPair()
this
)监视器,但它与ExplicitPairManager2.lock
不同getPair()
的结果因此有x!= y 所以最后没有关键部分。
换句话说,在修改时,您使用两个不同的对象进行同步:
ExplicitPairManager2.lock
撰写ExplicitPairManager2
(this
)的内部监视器,用于创建用于检查状态的副本