java 原始类型的易失性或同步性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1779258/
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-29 17:56:59  来源:igfitidea点击:

Volatile or synchronized for primitive type?

javamultithreadingatomicvolatilesynchronized

提问by DKSRathore

In Java, assignment is atomic if the size of the variable is less than or equal to 32 bits but is not if more than 32 bits.

在 Java 中,如果变量的大小小于或等于 32 位,则赋值是原子的,但如果大于 32 位则不是。

What (volatile/synchronized) would be more efficient to use in case of double or long assignment?

在双重或长分配的情况下,使用什么(易失性/同步)会更有效?

Like,

喜欢,

  volatile double x = y;

synchronized is not applicable with primitive argument. How do I use synchronized in this case? Of course I don't want to lock my class, so thisshould not be used.

同步不适用于原始参数。在这种情况下如何使用同步?当然我不想锁定我的班级,所以this不应该使用。

采纳答案by Yishai

If you find locking on the object itself too heavy, then synchronized is the way to go. Prior to Java 1.5 volatile may have been a good choice, but now volatile can have a very large impact by forcing instruction ordering on the method where the assignment happens. Create a separate object (private final Object X_LOCK = new Object();) and synchronize on it when setting or getting the value of that double. This will give you a fine level of control over the locking, which it seems that you need.

如果您发现锁定对象本身太重,那么同步就是要走的路。在 Java 1.5 之前 volatile 可能是一个不错的选择,但现在 volatile 可以通过强制指令对发生赋值的方法进行排序来产生非常大的影响。创建一个单独的对象 ( private final Object X_LOCK = new Object();) 并在设置或获取该双精度值时对其进行同步。这将为您提供对锁定的精细控制,这似乎是您需要的。

In the new concurrency package there are more options, such as AtomicReference which may be a good replacement for volatile if you really need to avoid synchronization.

在新的并发包中有更多选项,例如 AtomicReference,如果您确实需要避免同步,它可能是 volatile 的一个很好的替代品。

回答by oxbow_lakes

What are you trying to do? The synchronizedand volatilekeywords are mechanisms in Java which can be used to ensure that consistent values are observed by different threads reading the same data. In particular they allow you to reason about happens-beforerelations in your programs.

你想做什么?的synchronizedvolatile关键字是Java机制,其可用于确保一致的值由不同的线程读取相同数据中观察到。特别是它们允许您对程序中的发生之前关系进行推理。

You simply cannot avoid using one of volatileor synchronizedin order to properly access non-finalfields in a multi-threaded program. That said, the main reason that you are likely to require synchronizedover volatileis the requirement for using atomic compare and setoperations (i.e. it will not be any performance consideration). For example, in a multi-threaded program:

为了在多线程程序中正确访问非字段,您根本无法避免使用volatilesynchronized之一final。这就是说,主要的原因是,你很可能需要synchronizedvolatile是利用原子的要求比较和设定操作(即它不会有任何性能的考虑)。例如,在一个多线程程序中:

volatile int i = 0;

public void foo() { 
    if (i == 0) i = i + 1;
}

The above code is inherently unsafe, even though the variable's declaration as being volatile means that reads and writes are flushed to main memory - the only safe implementation of such a method would be something like:

上面的代码本质上是不安全的,即使变量声明为 volatile 意味着读取和写入被刷新到主内存 - 这种方法的唯一安全实现是这样的:

int i = 0;

public synchronized void foo() {
    if (i == 0) i = i + 1;
}

So which should you prefer? Well, if you have multiple threads modifying a field dependent on that field's value (i.e. compare-and set), then synchronizedis the only safe solution.

那么你应该更喜欢哪个?好吧,如果您有多个线程根据该字段的值修改一个字段(即比较并设置),那么这synchronized是唯一安全的解决方案。

It's also worth saying: the performance overhead of synchronizedis not a problem(in the overwhelming majority of cases). Synchronization-performance issues are usually due to unnecessary code bottlenecks, deadlocks or livelocks and can be mitigated if necessary. Any pure clock-cyclesoverhead will be dwarfed by other things you application does: file IO, database queries, remoting etc.

还值得一提synchronized性能开销不是问题(在绝大多数情况下)。同步性能问题通常是由于不必要的代码瓶颈、死锁或活锁造成的,必要时可以缓解。任何纯时钟周期开销都将与您应用程序所做的其他事情相形见绌:文件 IO、数据库查询、远程处理等。

回答by Matt Crinklaw-Vogt

volatile is certainly the way to go if you are only doing an assignment. I'm sure you know, but since it was brought up: if you would like to do more complex operations (increment the value for example) you would need to syncrhonize. i++ is never thread safe for any type of variable. You need to synch. i++ and the like since that is actually more than 1 operation.

如果你只是在做一个任务, volatile 肯定是要走的路。我相信你知道,但自从它被提出以来:如果你想做更复杂的操作(例如增加值),你需要同步。对于任何类型的变量,i++ 从来都不是线程安全的。你需要同步。i++ 等,因为这实际上是不止 1 个操作。

Not: It was expressed that you could use AtomicDouble but there is currently no AtomicDouble in java.util.concurrent.atomic

不是:有人表示您可以使用 AtomicDouble 但目前 java.util.concurrent.atomic 中没有 AtomicDouble

If you are doing a multiple operations on x, that requires setting it to a new value at the end, it is possible to do this in a thread safe manner with no locking what so ever, and have it be thread safe, using compare and set. Example:

如果您在 x 上执行多个操作,这需要在最后将其设置为一个新值,则可以以线程安全的方式执行此操作而无需锁定任何内容,并使其成为线程安全的,使用比较和放。例子:

AtomicLong x = new AtomicLong(SomeValue);
public void doStuff() {
  double oldX;
  double newX;
  do {
    oldX = x.get();
    newX = calculateNewX(oldX);
  } while (!x.compareAndSet
      (Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX)));

This works because compareAndSet will see if the value of x has changed since the last time you read it. If x has changed then you are forced to do the computation over again and re-try setting it.

这是有效的,因为 compareAndSet 将查看自上次读取 x 的值是否已更改。如果 x 已更改,则您将被迫重新计算并重新尝试设置它。

You could of course implement your own AtomicDouble instead of doing these doubleToLongBits conversions. Take a look at AtomicFieldUpdater.

您当然可以实现自己的 AtomicDouble 而不是进行这些 doubleToLongBits 转换。看看 AtomicFieldUpdater。

回答by Mirek Pluta

KDSRathore, you can use some explicit locks, or make some dummy Object object = new Object() , on which you synchronize in setter/getter of that double

KDSRathore,您可以使用一些显式锁,或制作一些虚拟 Object object = new Object() ,您可以在该 double 的 setter/getter 中同步

回答by Dmitry Sokolyuk

According to the oracle documentation, you can use volatile to refer to Double object:

根据oracle 文档,您可以使用 volatile 来引用 Double 对象:

volatile Double x = y;

"Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values."

“对引用的写入和读取始终是原子的,无论它们是作为 32 位还是 64 位值实现的。”