java AtomicInteger 和 volatile

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14338533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:55:35  来源:igfitidea点击:

AtomicInteger and volatile

javamultithreadingthread-safetyatomicvolatile

提问by Achow

I know volatileallows for visibility, AtomicIntegerallows for atomicity. So if I use a volatile AtomicInteger, does it mean I don't have to use any more synchronization mechanisms?

我知道volatile允许可见性,AtomicInteger允许原子性。因此,如果我使用 volatile AtomicInteger,是否意味着我不必再使用任何同步机制?

Eg.

例如。

class A {

    private volatile AtomicInteger count;

    void someMethod(){
        // do something
        if(count.get() < 10) {
            count.incrementAndGet();
        }
}

Is this threadsafe?

这是线程安全的吗?

回答by Jon Skeet

I believe that Atomic*actually gives bothatomicity and volatility. So when you call (say) AtomicInteger.get(), you're guaranteed to get the latestvalue. This is documented in the java.util.concurrent.atomicpackage documentation:

我相信这Atomic*实际上同时提供原子性和波动性。因此,当您调用 (say) 时AtomicInteger.get(),您一定会获得最新的值。这记录在java.util.concurrent.atomic包文档中

The memory effects for accesses and updates of atomics generally follow the rules for volatiles, as stated in section 17.4 of The Java? Language Specification.

  • get has the memory effects of reading a volatile variable.
  • set has the memory effects of writing (assigning) a volatile variable.
  • lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, > - lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.
  • weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet.
  • compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

原子的访问和更新的内存效应通常遵循 volatiles 的规则,如 The Java? 的 17.4 节所述。语言规范。

  • get 具有读取易失性变量的记忆效应。
  • set 具有写入(分配)易失性变量的记忆效应。
  • lazySet 具有写入(分配)易失性变量的内存效果,但它允许对后续(但不是先前)内存操作进行重新排序,这些操作本身不会对普通非易失性写入施加重新排序约束。在其他使用上下文中, > - lazySet 可能会在清空时应用,为了垃圾收集,一个永远不会再次访问的引用。
  • weakCompareAndSet 原子地读取和有条件地写入变量,但不会创建任何发生在排序之前,因此不提供关于之前或后续读取和写入除 weakCompareAndSet 目标之外的任何变量的任何保证。
  • compareAndSet 和所有其他读取和更新操作(例如 getAndIncrement)都具有读取和写入易失性变量的记忆效应。

Now if you have

现在如果你有

volatile AtomicInteger count;

the volatilepart means that each thread will use the latest AtomicIntegerreference, and the fact that it's an AtomicIntegermeans that you'll alsosee the latest value for that object.

volatile部分意味着每个线程将使用最新的AtomicInteger引用,事实上它AtomicInteger意味着您还将看到该对象的最新值。

It's not common (IME) to need this - because normally you wouldn't reassign countto refer to a different object. Instead, you'd have:

需要这个并不常见(IME) - 因为通常你不会重新分配count来引用不同的对象。相反,你有:

private final AtomicInteger count = new AtomicInteger();

At that point, the fact that it's a finalvariable means that all threads will be dealing with the same object - and the fact that it's an Atomic*object means they'll see the latest value within that object.

在这一点上,它是一个final变量的事实意味着所有线程将处理同一个对象 - 而它是一个Atomic*对象的事实意味着它们将看到该对象中的最新值。

回答by Mygod

I'd say no, it's not thread-safe, if you define thread-safe as having the same result under single threaded mode and multithreaded mode. In single threaded mode, the count will never go greater than 10, but in multithreaded mode it can.

我会说不,它不是线程安全的,如果您将线程安全定义为在单线程模式和多线程模式下具有相同的结果。在单线程模式下,计数永远不会超过 10,但在多线程模式下它可以。

The issue is that getand incrementAndGetis atomic but an ifis not. Keep in mind that a non-atomic operation can be paused at any time. For example:

问题是getandincrementAndGet是原子的,而 anif不是。请记住,可以随时暂停非原子操作。例如:

  1. count = 9currently.
  2. Thread A runs if(count.get() <10)and gets trueand stopped there.
  3. Thread B runs if(count.get() <10)and gets truetoo so it runs count.incrementAndGet()and finishes. Now count = 10.
  4. Thread A resumes and runs count.incrementAndGet(), now count = 11which will never happen in single threaded mode.
  1. count = 9目前。
  2. 线程 A 运行if(count.get() <10)并到达true并停在那里。
  3. 线程 B 运行if(count.get() <10)并得到true太多,因此它运行count.incrementAndGet()并完成。现在count = 10
  4. 线程 A 恢复并运行count.incrementAndGet(),现在count = 11在单线程模式下永远不会发生。

If you want to make it thread-safe without using synchronizedwhich is slower, try this implementation instead:

如果您想使其成为线程安全而不使用synchronized哪个较慢,请尝试使用以下实现:

class A{

final AtomicInteger count;

void someMethod(){
// do something
  if(count.getAndIncrement() <10){
      // safe now
  } else count.getAndDecrement(); // rollback so this thread did nothing to count
}

回答by NishantM

Answer is there in this code

答案在这段代码中

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java

This is source code of AtomicInteger. The value is Volatile. So,AtomicInteger uses Volatile inside.

这是 AtomicInteger 的源代码。该值是易失性的。所以,AtomicInteger 内部使用了 Volatile。

回答by Roger Beardsworth

To maintain the original semantics, and support multiple threads, you could do something like:

要保持原始语义并支持多线程,您可以执行以下操作:

public class A {

    private AtomicInteger count = new AtomicInteger(0);

    public void someMethod() {

        int i = count.get();
        while (i < 10 && !count.compareAndSet(i, i + 1)) {
            i = count.get();
        }

    }

}

This avoids any thread ever seeing count reach 10.

这避免了任何线程看到计数达到 10。

回答by JavCray

Your query can be answered in 2 parts, because there are 2 questions in your query :

您的查询可以分两部分回答,因为您的查询中有 2 个问题:

1) Referring to Oracle's tutorial documentation for Atomic variables : https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html

1) 参考 Oracle 的原子变量教程文档:https: //docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html

The java.util.concurrent.atomic package defines classes that support atomic operations on single variables. All classes have get and set methods that work like reads and writes on volatile variables. That is, a set has a happens-before relationship with any subsequent get on the same variable. The atomic compareAndSet method also has these memory consistency features, as do the simple atomic arithmetic methods that apply to integer atomic variables.

java.util.concurrent.atomic 包定义了支持对单个变量进行原子操作的类。所有类都有 get 和 set 方法,它们的工作方式类似于对 volatile 变量的读取和写入。也就是说,一个集合与同一个变量上的任何后续 get 都有一个发生在之前的关系。原子 compareAndSet 方法也具有这些内存一致性特性,就像应用于整数原子变量的简单原子算术方法一样。

So atomic integer does use volatile inside, as other answers here have mentioned. So there's no point in making your atomic integer volatile. You need to synchronize your method.

所以原子整数确实在内部使用了 volatile ,正如这里的其他答案所提到的。所以让你的原子整数易变是没有意义的。您需要同步您的方法。

You should watch John Purcell's free video on Udemy , where he shows the failure of volatile keyword when multiple threads are trying to modify it. Simple and beautiful example. https://www.udemy.com/course/java-multithreading/learn/lecture/108950#overview

您应该在 Udemy 上观看 John Purcell 的免费视频,其中展示了当多个线程尝试修改 volatile 关键字时失败的情况。简单而美丽的例子。 https://www.udemy.com/course/java-multithreading/learn/lecture/108950#overview

If you change the volatile counter in John's example into an atomic variable, his code is guaranteed to succeed without using sunchronized keyword like he has done in his tutorial

如果您将 John 示例中的 volatile 计数器更改为原子变量,则他的代码可以保证成功,而无需像他在教程中所做的那样使用 sunchronized 关键字

2) Coming to your code : Say thread 1 kicks into action and "someMethod" does a get and checks for size. It is possible that before getAndIncrement executes(say, by thread 1) , another thread (say thread 2)kicks in and increases the count to 10, and gets out; after which, your thread 1 will resume and increase count to 11. This is erroneous output. This is because your "someMethod" is not protected in anyway from synhronization problems. I would still recommend you to watch john purcell's videos to see where volatile fails , so that you have a better understanding of the keyword volatile. Replace it with atomicinteger in his example and see the magic.

2) 进入您的代码:假设线程 1 开始执行,“someMethod”执行获取并检查大小。有可能在 getAndIncrement 执行(例如,由线程 1)之前,另一个线程(例如线程 2)开始并将计数增加到 10,然后退出;之后,您的线程 1 将恢复并将计数增加到 11。这是错误的输出。这是因为您的“someMethod”在任何情况下都不受同步问题的保护。我仍然建议您观看 john purcell 的视频以了解 volatile 在哪里失败,以便您对关键字 volatile 有更好的理解。在他的例子中用 atomicinteger 替换它,看看魔法。