Java中的同步 - 用Java实例思考

时间:2017-10-28 16:42:35

标签: java multithreading

我现在阅读Thinking in 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的奇数值。

为什么我得到这个输出。如何工作synchronized

2 个答案:

答案 0 :(得分:2)

您的最终示例return currentEventValue;语句不在synchronized块内。因此,假设线程A和线程B都调用next()

主题A:

  • 同步,
  • 增量currentEventValue(现在值很奇怪)
  • 增量currentEventValue(价值甚至再次)
  • 离开synchronized块。

主题B:

  • 同步
  • 增量currentEventValue(现在值很奇怪)

主题A:

  • 返回currentEventValue(单数)

主题B:

  • 增量currentEventValue(价值甚至再次)
  • 离开synchronized区块。
  • 返回偶数值。

答案 1 :(得分:1)

  • currentEvenValue是342
  • 主题1进入同步块
  • 线程2尝试进入同步块但必须等待
  • 线程1将currentEvenValue递增两次,因此该值现在为344
  • 线程1离开同步块
  • 线程2进入同步块并第一次递增currentEvenValue,因此该值现在为345
  • 线程1读取currentEvenValue的值,返回它并打印它:345

规则很简单:必须同步对共享状态(读或写)的所有访问。