我研究了synchronized
和volatile
之间的区别,并编写了一个没有volatile的示例,我认为这与具有volatile的示例是等效的(由于在获取时进行了额外的同步)
易失性版本
public class Volatile extends Super {
private volatile int xCounter;
private volatile int yCounter;
@Override
public int getXCounter() {
return this.xCounter;
}
@Override
public int getYCounter() {
return this.yCounter;
}
@Override
public void incrementXCounter() {
synchronized (getLockXCounter()) {
this.xCounter++;
}
}
@Override
public void incrementYCounter() {
synchronized (getLockYCounter()) {
this.yCounter++;
}
}
}
版本无波动
public class NonVolatile extends Super {
private int xCounter;
private int yCounter;
@Override
public int getXCounter() {
synchronized (getLockXCounter()) {
return this.xCounter;
}
}
@Override
public int getYCounter() {
synchronized (getLockYCounter()) {
return this.yCounter;
}
}
@Override
public void incrementXCounter() {
synchronized (getLockXCounter()) {
this.xCounter++;
}
}
@Override
public void incrementYCounter() {
synchronized (getLockYCounter()) {
this.yCounter++;
}
}
}
超级类
public abstract class Super {
private final Object lockXCounter = new Object();
private final Object lockYCounter = new Object();
protected abstract int getXCounter();
protected abstract int getYCounter();
protected abstract void incrementXCounter();
protected abstract void incrementYCounter();
Object getLockXCounter() {
return lockXCounter;
}
Object getLockYCounter() {
return lockYCounter;
}
}
具体来说,在以下两点提供的两个示例之间是否等效?
1)效果
2)线程安全
请提供一个示例,说明如果其中一个示例不能保证线程安全,则会显示线程安全性不足。
答案 0 :(得分:1)
volatile
被认为是较弱的同步形式。您使用volatile来确保读取时当前值的可见性,因此您将锁定用于所有可变操作,将volatile用于只读操作。在锁只允许一个线程一次访问一个值的情况下,易失性读取则允许一个以上的访问,因此,当您使用volatile保护读取代码路径时,与对所有代码使用锁定相比,您获得的共享程度更高路径。因此,前者的性能优于后者。
答案 1 :(得分:1)
简单地说,第一个对作家来说是缓慢的,对读者来说是快速的,第二个对两者都是平衡的(假设)。您应该注意,在某些体系结构上,spark-submit
与实现中的同步本质上是相同的,因为底层系统仍需要保持内存一致性,因此就性能而言,使用任何一个示例也可能无关紧要。 。所有这些,只是我在空中挥舞着做个假想,您应该在目标系统上进行测试和基准测试,并亲自观察一下。立即测量,以后再优化。
是的,它是线程安全的。首先是较弱的一致性,因为在复合更新期间您可能会读取,所以不能总是保证看到最新的值,但是只要编写者不互相干扰,因为它可以最多只能比“实际”值大1。当然,有时保持高度一致很重要,因此您应该再次考虑将这种因素如何影响到您自己的目标系统中。
等效性很难回答,但是“取决于情况”。同样,有时volatile
与基础系统上的volatile
基本相同,但是JMM并不能真正保证这一点。可以保证写操作对读者是可见的,相对于其他写操作(而不是读操作),写操作是原子的。
最后,您似乎受到性能的激励,但是您应该注意synchronized
不是免费的。同样,我要强调的是,它仍然需要同步底层硬件,无论是锁还是volatile
,这都是“相对”昂贵的。
或者您可以只使用volatile
并获得两全其美:快速读取和快速写入(在某些情况下,您应该考虑AtomicInteger
以获得高竞争)。选择确实是您的。