在Java中,如果变量的大小小于或等于32位,则赋值是原子的,但如果大于32位则不是。
在双重或长期分配的情况下,什么(易变/同步)会更有效?
像,
volatile double x = y;
synchronized不适用于原始参数。在这种情况下如何使用synchronized?当然我不想锁定我的课程,所以不应该使用this
。
答案 0 :(得分:26)
你想做什么? synchronized
和volatile
关键字是Java中的机制,可用于确保读取相同数据的不同线程观察到一致的值。特别是它们允许您在程序中推理发生在之前的关系。
您无法避免使用volatile
或synchronized
之一来正确访问多线程程序中的非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位值。"