2017-10-28 84 views
0

我现在读了一本书Thinking in Java,关于关键部分的章节,我无法理解一个例子,因为我收到了本书中没有描述的例外。示例如下所示:关键部分 - 用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 

这个例子无一例外执行。

在上面的例子中,我明白它是如何工作的,但问题在于显式锁定的例子。 实例与书中明确锁定:

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#增量,如果他使用也锁定对象?这是书中的错误吗?

第二个问题是我不明白为什么我得到异常?

错误时抛出被扔在:

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 
     } 
    } 
} 

为什么我excpetions和作者不?这种可能的JVM行为在不同的系统上有所不同吗?我使用Ubuntu 16.04 LTS和Java 8.

+0

'storage'变量的用途是什么? –

回答

0

如果您想为多个线程建立关键部分,则需要在同一个对象上进行同步。

您的异常正在抛出,对于ExplicitPairManager2中的修改对进行修改。

让我们来看看异常引起的流动怎么可能是这样的:

  1. ExplicitPairManager2.lock.lock()被收购
  2. ExplicitPairManager2.p.incrementX()发生
  3. PairChecker调用getPair()
  4. PairChecker获得pairManager的内部(this)显示器,但它是不同于ExplicitPairManager2.lock
  5. 结果getPair()因此有x!= y

所以最后没有关键部分。

换句话说,当修改,你使用两个不同的对象进行同步:

  • ExplicitPairManager2.lock
  • ExplicitPairManager2this)内部监控,以检查状态
创建一个副本