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

时间:2009-11-22 17:02:15

标签: java multithreading atomic volatile synchronized

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

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

像,

  volatile double x = y;

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

5 个答案:

答案 0 :(得分:26)

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

您无法避免使用volatilesynchronized之一来正确访问多线程程序中的非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;
}

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:3)

如果你只做一个任务,那么

volatile肯定是要走的路。 我相信你知道,但是因为它被提出来了:如果你想做更复杂的操作(例如增加值),你需要同步。对于任何类型的变量,i ++绝不是线程安全的。你需要同步。 i ++之类的,因为那实际上是一次以上的操作。

不是:有人表示可以使用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。

答案 3 :(得分:2)

KDSRathore,你可以使用一些显式锁,或者做一些虚拟的Object object = new Object(),你可以在那个双重的setter / getter中进行同步

答案 4 :(得分:0)

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

volatile Double x = y;

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