有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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


共 (2) 个答案

  1. # 1 楼答案

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

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

  2. # 2 楼答案

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

    线程A:

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

    线程B:

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

    线程A:

    • 返回currentEventValue(奇数)

    线程B:

    • 增量currentEventValue(值再次为偶数)
    • 离开synchronized
    • 返回偶数值