当前位置: 首页 > 知识库问答 >
问题:

如何使volatile count操作成为线程安全的

程枫
2023-03-14

我一直在浏览JCIP,作者说。。

线程限制的一种特殊情况适用于可变变量。对共享的易失性变量执行读-修改-写操作是安全的,只要您确保该易失性变量仅从单个线程写入

例如,实例计数被认为是一个复合操作(读取值,向其添加一个值,并更新值),声明计数为易失性不会使此操作原子化,因此线程安全在这里得不到保证!!我说得对吗??但是在这里,作者说如果我们确保只从单个线程写入易失性变量,我们就可以修复它。我不明白这一点。请举例说明。

共有3个答案

尚鸿才
2023-03-14

这背后的想法是,为了避免数据竞争,只有一个写入线程,然后Java内存模型通过before-before关系处理可见性保证。如果有多个写线程,那么仅仅通过将变量声明为“volatile”就无法使隐式复合操作原子化

17.4.4。同步顺序

对易失性变量v的写入(§8.3.1.4)与任何线程对v的所有后续读取同步(其中“后续”是根据同步顺序定义的)。

岳永思
2023-03-14

典型的用例可以是这样的:

public class Foo implements Runnable {
    private volatile int itemsProcessed;

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (true) { //this is just an example so we don't care about stopping
            processItem(); //process a single item
            itemsProcessed++;
        }
    }
}

现在,其他人可以查询线程的进度而无需额外的同步,但只允许线程本身更新它。对于其他人,该字段是只读的。

如果没有易失性,其他线程可能根本看不到任何更改,甚至可能看到它们以古怪的顺序,有时增加,有时减少。

多个线程在不进行任何额外同步的情况下可以写入volatile变量的唯一时间是,如果写入是幂等的(也就是说,多个写入与单个写入具有相同的效果)。这可以在用于停止线程的模式中看到:

public class Foo implements Runnable {
    private volatile boolean stopped = false;

    public void stopProcessing() {
        stopped = true;
    }

    public int getItemsProcessed() { return itemsProcessed; }

    @Override
    public void run() {
        while (!stopped) { 
            processItem(); //process a single item
        }
    }
}

在这里,其他线程唯一能做的就是将stopped设置为true。不管他们中有多少人这样做,顺序如何,结果总是一样的。

施阳夏
2023-03-14

计数本质上是两个操作,读取计数的值和存储计数1。

如果两个线程试图同时进行count,它们可能都会读取count的旧值并将其递增一。因此,在最后,如果不添加任何同步机制,最终的结果将是计数1,而不是计数2。

Volatile不能保证线程在这种情况下的安全性(不是原子的),它只能保证最近存储的值对所有线程都是可用的。但这在上述场景中没有多大帮助。您需要确保,没有其他线程将读取之前的新计数的旧值存储。

如果需要线程安全计数器,我建议您查看AtomicInteger(以及类似的)类。它们提供了计数的原子版本;incrementAndGet方法。

 类似资料:
  • 问题内容: 据我所知,这是可变的,因此,如果多个线程试图访问和修改它,则不是线程安全的。我们如何使用客户端锁定或组合(包装器)使其成为线程安全的? 问题答案: 按照从好到坏的顺序: 根本不使用它,请查看Java 8的新Date and Time API。 完全不使用它,请查看jodatime 在所有的,使用不使用或一成不变的原始与代表 纪元时间 封装它。始终返回的防御性副本,从不引用内部对象 在实

  • 问题内容: 我是Go的新手,我需要创建一个线程安全的变量。我知道在Java中您只能使用关键字,但是似乎没有类似的东西存在。有什么方法可以同步变量? 问题答案: Java中的一种意思是仅允许单个线程(在任何给定时间)执行代码块。 在Go中,有很多构造可以实现该目标(例如互斥体,通道,等待组,中的原语),但是Go的谚语是: “不要通过共享内存进行通信;而是通过通信来共享内存。” 因此,不要锁定和共享变

  • 本文向大家介绍如何在Java中使类成为线程安全的?,包括了如何在Java中使类成为线程安全的?的使用技巧和注意事项,需要的朋友参考一下 线程安全类是保证从多个线程并发调用时正确的类的内部状态以及方法返回的值的类。 HashMap是一个非同步的集合类。如果我们需要对其执行线程安全操作,则必须显式同步它。 例: 在上面的示例中,我们有一个HashMap,它具有整数键和String类型值。为了使其同步,

  • 我只是一个非开发人员,所以我的问题可能非常简单! 我只是在测试Java多线程的东西,这不是真正的代码。我想知道如何在 Java 中同时更新两个成员变量,以防我们希望它们都同步。举个例子: 在这种情况下(当然,想象一下多线程),我希望能够保证对< code>items和< code>itemToStatus的任何读取总是返回相同的结果。 因此,如果代码在< code>itemToStatus.put

  • 我有一个简单的静态日志记录类。但是,它肯定不是线程安全的,因为每个调用都试图写入同一个文件。我得到了这些例外情况: 什么是最好的方法使它的线程安全? 作为一个日志记录函数,我希望能够从代码的许多不同部分访问它(因此,我选择它为静态的原因)。然而,我想要使它成为线程安全的,我总是必须向它传递一个公共对象来lock(),我认为这违背了静态函数的目的。还是事实并非如此?

  • 问题内容: 我看过OpenJDK的OpenJDK源代码,似乎所有写操作都受同一锁保护,而读操作则根本不受保护。据我了解,在JMM下,对变量的所有访问(读和写)都应受锁保护,否则可能会发生重新排序的效果。 例如,method包含以下几行(处于锁定状态): 另一方面,该方法仅起作用。 在我对JMM的理解中,这意味着如果将语句1-4重新排序为1-2(new)-4-2(copyOf)-3 ,则可能会在不一