我正在学习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++;
    }
}

是否强制声明计数器变量为易失性?

推荐答案

为了使Counter类的行为正确,您需要将counter字段标记为volatile,或者您需要将getCounter()方法也标记为synchronized,或者删除Counter类并使用AtomicInteger.

在您的代码的特定示例中,它可能会正确工作,但这是-同样可能-因为您对Executor服务调用awaitTermination,提交的任务可能会在超时到期之前完成.我认为,但不确定的是,这将在执行器线程上执行的任务之间建立一种先发制人的关系,因此您的主线程将确保看到最新的值counter.

然而,如果没有这样的事前关系,主线程(或任何其他线程)不能保证看到最新的值counter,它可以看到任何中间状态,包括初始值0.要建立这样的发生之前,您将需要标记字段volatile,或者在方法上使用synchronized,因为这将保证读取最新写入的值.

顺便说一句,调用awaitTermination而不调用Executor服务上的shutdown()是不正确的.

Java相关问答推荐

当切换javaFX场景时,stage的大小正在Minimize

JUnit—如何模拟局部变量对象方法调用

在模拟超类中设置非setter属性的值

为什么我的ArrayList索引的索引总是返回-1?

Java中是否有某种类型的池可以避免重复最近的算术运算?

Java中实现的归并排序算法给出ArrayIndexOutOfBound异常

为什么Java Annotation接口覆盖对象类中的方法

当返回Mono<;Something>;时,不会调用Mono<;void>;.flatMap

如何在Java记录中设置BigDecimal类型属性的精度?

在添加AdMob时无法为Google Play构建应用程序包:JVM垃圾收集器崩溃和JVM内存耗尽

Spring安全令牌刷新和JWT签名与本地计算的签名不匹配

没有使用Lombok生成的参数

buildDir:File!&#的getter解决方案是什么?39.被抛弃

Regex以查找不包含捕获组的行

Cordova Android Gradle内部版本组件不兼容

如何使用Criteria Builder处理一对多关系中的空值?

每次我需要时创建和关闭数据库连接会有什么效果吗?

如何使用Java ZoneID的区域设置?

为什么我的登录终结点不能被任何请求访问?

Cucumber java-maven-示例表-未定义一步