当我在 Java 中使用同步块来递增 int 时,是否必须声明变量 volatile?

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

我正在学习 Java 中的并发性,并且正在观看 YouTube 视频。这是关于多线程环境下增量操作的解释。解决方案可能是一个易失性变量或一个同步块。但我的问题是,如果我使用同步块,我是否还需要使用易失性变量。如果我有一个同步块还不够吗?我看到讲师使方法同步,并且使变量可变。在我看来,使方法同步就足够了,因为当线程进入同步方法时,它会从主内存中获取变量的值,而当线程退出同步方法时,它会将值直接发送到主内存,并且该值可用于其他线程。所以我认为不必强制使变量为易失性。

这是代码:

public class ClientTest {
    public static void main(String[] args) {
        ExecutorService executorService = null;
        Counter counter = new Counter();

        try {
            executorService = Executors.newFixedThreadPool(2);

            Runnable task1 = () -> {
                for (int i = 1; i <= 20000; i++) {
                    counter.increment();
                }
            };

            Runnable task2 = () -> {
                for (int i = 1; i <= 80000; i++) {
                    counter.increment();
                }
            };

            executorService.submit(task1);
            executorService.submit(task2);
            executorService.awaitTermination(1, TimeUnit.SECONDS);

            System.out.println("Final counter value: " + counter.getCounter());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}


public class Counter {
    private int counter;
    public int getCounter() {
        return counter;
    }
    public synchronized void increment() {
        counter++;
    }
}

计数器变量必须声明为 volatile 吗?

java multithreading volatile synchronized
1个回答
0
投票

为了

Counter
类的正确行为,您需要标记
counter
字段
volatile
,或者还需要创建
getCounter()
方法
synchronized

在代码的具体示例中,它可能会正常工作,但这同样可能是因为您在执行器服务上调用

awaitTermination
,并且提交的任务可能会在超时到期之前完成。我认为,但不确定,这将在执行器线程上执行的任务之间建立发生之前的关系,因此您的主线程将保证看到
counter
的最新值。

但是,如果没有这种先于关系,主线程(或任何其他线程)不能保证看到

counter
的最新值。要建立这样的发生之前,您需要标记字段
volatile
,或在方法上使用
synchronized

顺便说一句,在执行器服务上调用

awaitTermination
而不调用
shutdown()
是不正确的。

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