java - Java 中的同步 - Thinking in Java 示例

标签 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 的奇数值。

为什么我得到这个输出。如何同步工作?

最佳答案

最后一个示例的 return currentEventValue; 语句不在 synchronized block 内。因此,假设线程 A 和线程 B 都调用 next():

线程A:

  • 同步,
  • 增量currentEventValue(现在的值是奇数)
  • 增量currentEventValue(值再次为偶数)
  • 离开同步 block 。

线程B:

  • 同步
  • 增量currentEventValue(现在的值是奇数)

线程A:

  • 返回currentEventValue(奇数)

线程B:

  • 增量currentEventValue(值再次为偶数)
  • 离开同步 block 。
  • 返回偶数值。

关于java - Java 中的同步 - Thinking in Java 示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46992111/

相关文章:

c - 为什么2个线程的执行比C中的1个线程慢?

java - 如何停止崩溃的JLabels

python - 为什么python线程数是2开头?

python - 尽管使用线程,PyQt5 窗口仍然卡住

java - 属性类构造函数的描述含义

java - 应用程序不适合所有显示尺寸

python - 在 django View 中使用 subprocess.Popen() 执行 python 脚本

java - 设置并发> 1后spring kafka thorws InstanceAlreadyExistsException异常

java - 如何从 HttpServlet 调用 RemoteServiceServlet 中的方法?

使用 Maven 构建 Java 代码并将 war 存储在 nexus 中并且必须部署到应用服务器