Java并发实践“清单7.1。使用易失性字段来保持取消状态。”。是否同步可见性?

问题描述 投票:0回答:1

我正在实践中阅读Java Concurrency,并遇到以下代码片段。我认为synchronized的使用是为了提高可见性(让调用generator.get()的线程查看最新的primes),因为任务PrimeGenerator由一个线程执行,而内部primes与其他线程不共享线程。

我对吗?

清单7.1。使用易失性字段来保持取消状态。

@ThreadSafe
public class PrimeGenerator implements Runnable {
    @GuardedBy("this")
    private final List<BigInteger> primes
            = new ArrayList<BigInteger>();
    private volatile boolean cancelled;

    public void run() {
        BigInteger p = BigInteger.ONE;
        while (!cancelled) {
            p = p.nextProbablePrime();
            synchronized (this) {
                primes.add(p);
            }
        }
    }

    public void cancel() {
        cancelled = true;
    }

    public synchronized List<BigInteger> get() {
        return new ArrayList<BigInteger>(primes);
    }
}

清单7.2。产生一秒钟的素数。

List<BigInteger> aSecondOfPrimes() throws InterruptedException {
    PrimeGenerator generator = new PrimeGenerator();
    new Thread(generator).start();
    try {
        SECONDS.sleep(1);
    } finally {
        generator.cancel();
    }
    return generator.get();
}
java multithreading synchronized
1个回答
0
投票

在多线程中,synchronized用于并发,而不是可见性。在单线程中,synchronized是多余的

在您的示例中,仅创建一个Thread。因此,是的,您可能认为不需要使用synchronized。但是,如果运行两个Thread:线程A仍在调用PrimeGenerator::run而线程B在PrimeGenerator::get中该怎么办?

那时发生的事情是B正在构造一个新的集合,并迭代所有值以将它们复制到新的集合中。同时,A正在添加新的值。 B的预期结果是什么?你怎么知道的?最好的情况是get对其他线程说“一直等到我完成复制之前再修改primes”。

如果仅同步primes.add(p),将不会解决任何问题,因为修改primes本身就是问题。如果仅同步get,则add的内部将破坏primes集合。

所以两个synchronized都是必需的。只需考虑在7.2清单示例之外查看大图。如果PrimeGenerator仅在7.2列表中使用,则不会发生任何问题,但这是不可重用的。

© www.soinside.com 2019 - 2024. All rights reserved.