原始类型是易失性的还是同步的?

发布于 2024-08-12 10:11:37 字数 254 浏览 4 评论 0原文

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

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

例如,

  volatile double x = y;

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

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.

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 this should not be used.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

生死何惧 2024-08-19 10:11:37

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

为了正确访问多线程程序中的非final 字段,您根本无法避免使用易失性同步 之一。也就是说,您可能需要 synchronized 而不是 volatile 的主要原因是需要使用原子比较和设置操作(即 >这不会是任何性能考虑)。例如,在多线程程序中:

volatile int i = 0;

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

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

int i = 0;

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

:你应该选择哪个?那么,如果您有多个线程根据字段的值修改字段(即比较和设置),那么同步是唯一安全的解决方案。

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

What are you trying to do? The synchronized and volatile keywords 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-before relations in your programs.

You simply cannot avoid using one of volatile or synchronized in order to properly access non-final fields in a multi-threaded program. That said, the main reason that you are likely to require synchronized over volatile is the requirement for using atomic compare and set operations (i.e. it will not be any performance consideration). For example, in a multi-threaded program:

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:

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 synchronized is the only safe solution.

It's also worth saying: the performance overhead of synchronized is 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-cycles overhead will be dwarfed by other things you application does: file IO, database queries, remoting etc.

夏末染殇 2024-08-19 10:11:37

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

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

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.

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.

ぶ宁プ宁ぶ 2024-08-19 10:11:37

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

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

如果您正在对 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)));

这是有效的,因为compareAndSet 将查看 x 的值自上次读取以来是否已更改。如果 x 发生了变化,那么您将被迫重新进行计算并重新尝试设置它。

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

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.

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

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:

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.

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

固执像三岁 2024-08-19 10:11:37

KDSRaore,您可以使用一些显式锁,或者创建一些虚拟 Object object = new Object() ,在该双精度的 setter/getter 中进行同步

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

缪败 2024-08-19 10:11:37

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

volatile Double x = y;

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

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

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."

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文