2017-10-28 105 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)內部監控,以檢查狀態
創建一個副本