2017-10-28 70 views
0

我現在閱讀在Java中思考,有關同步的章節,有一個我無法理解的例子。Java中的同步 - Java示例中的思考

public abstract class IntGenerator { 

    private volatile boolean canceled = false; 

    public abstract int next(); 

    public void cancel() { 
     canceled = true; 
    } 

    public boolean isCanceled() { 
     return canceled; 
    } 
} 

public class EvenGenerator extends IntGenerator { 

    private int currentEvenValue = 0; 

    final Object object = new Object(); 

    @Override 
    public int next() { 
     ++currentEvenValue; 
     ++currentEvenValue; 
     return currentEvenValue; 
    } 

    public static void main(String[] args) { 
     EvenChecker.test(new EvenGenerator()); 
    } 
} 

public class EvenChecker implements Runnable { 

    private IntGenerator generator; 
    private final int id; 

    public EvenChecker(IntGenerator generator, int id) { 
     this.generator = generator; 
     this.id = id; 
    } 

    @Override 
    public void run() { 
     while (!generator.isCanceled()) { 
      int val = generator.next(); 
      if (val % 2 != 0) { 
       System.out.println(val + " odd"); 
       generator.cancel(); 
      } 
     } 
    } 

    public static void test(IntGenerator generator, int count) { 
     System.out.println("To finish press Ctrl + C"); 
     final ExecutorService executorService = Executors.newCachedThreadPool(); 
     for (int i = 0; i < count; i++) { 
      executorService.execute(new EvenChecker(generator, i)); 
     } 
    } 

    public static void test(IntGenerator generator) { 
     test(generator, 10); 
    } 
} 

而且例子輸出爲:

1239 odd 
1237 odd 
1239 odd 

,我理解這一點。這意味着3個線程在第一次增量後讀取currentValue。這個問題的

解決辦法是:沒有錯誤

public class SynchronizedEvenGenerator extends IntGenerator { 

    private int currentEvenValue = 0; 

    @Override 
    public synchronized int next() { 
     ++currentEvenValue; 
     Thread.yield(); 
     ++currentEvenValue; 
     return currentEvenValue; 
    } 

    public static void main(String[] args) { 
     EvenChecker.test(new SynchronizedEvenGenerator()); 
    } 
} 

現在程序正在無窮大。 我想只有增量以這種方式同步:

public class SynchronizedEvenGenerator extends IntGenerator { 

    private int currentEvenValue = 0; 

    @Override 
    public int next() { 
     synchronized (this) { 
      ++currentEvenValue; 
      Thread.yield(); 
      ++currentEvenValue; 
     } 
     return currentEvenValue; 
    } 

    public static void main(String[] args) { 
     EvenChecker.test(new SynchronizedEvenGenerator()); 
    } 
} 

但現在例子出來放是:

345 odd 

而且我不明白爲什麼它可以讀取CurrentValue的,如果兩個增量奇數值是同步的,並且任何線程都無法在第一個和第二個增量之間讀取currentValue。

爲什麼我得到這個輸出。工作如何​​?

回答

2

您的最終示例的return currentEventValue;語句不在​​塊內。因此,假設線程A和線程B都調用next()

線程A:

  • 同步,
  • 增量currentEventValue(值現在爲奇數)
  • 增量currentEventValue(價值甚至再次)
  • 離開​​區塊。

線程B:

  • 同步
  • 增量currentEventValue(值現在爲奇數)

線程A:

  • 返回currentEventValue(奇數)

線程B:

  • 增量currentEventValue(值甚至再次是)
  • 離開​​塊。
  • 返回一個偶數值。
1
  • currentEvenValue是342
  • 線程1進入同步塊
  • 線程2名試圖進入同步塊但必須等待
  • 線程1倍的增量currentEvenValue兩次,所以現在的值是344
  • 線程1離開同步塊
  • 線程2進入同步塊並且第一次遞增currentEvenValue,所以現在的值爲:
  • 線程1讀取currentEvenValue的值,返回它,並打印:345

規則是簡單的:所有訪問一個共享的狀態,讀或寫,必須同步。