这两个示例是否等效(易失性+同步增量与同步提取+增量)?

时间:2018-09-26 02:39:31

标签: java multithreading locking synchronized volatile

我研究了synchronizedvolatile之间的区别,并编写了一个没有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)线程安全

请提供一个示例,说明如果其中一个示例不能保证线程安全,则会显示线程安全性不足。

2 个答案:

答案 0 :(得分:1)

volatile被认为是较弱的同步形式。您使用volatile来确保读取时当前值的可见性,因此您将锁定用于所有可变操作,将volatile用于只读操作。在锁只允许一个线程一次访问一个值的情况下,易失性读取则允许一个以上的访问,因此,当您使用volatile保护读取代码路径时,与对所有代码使用锁定相比,您获得的共享程度更高路径。因此,前者的性能优于后者。

答案 1 :(得分:1)

  1. 简单地说,第一个对作家来说是缓慢的,对读者来说是快速的,第二个对两者都是平衡的(假设)。您应该注意,在某些体系结构上,spark-submit与实现中的同步本质上是相同的,因为底层系统仍需要保持内存一致性,因此就性能而言,使用任何一个示例也可能无关紧要。 。所有这些,只是我在空中挥舞着做个假想,您应该在目标系统上进行测试和基准测试,并亲自观察一下。立即测量,以后再优化。

  2. 是的,它是线程安全的。首先是较弱的一致性,因为在复合更新期间您可能会读取,所以不能总是保证看到最新的值,但是只要编写者不互相干扰,因为它可以最多只能比“实际”值大1。当然,有时保持高度一致很重要,因此您应该再次考虑将这种因素如何影响到您自己的目标系统中。

等效性很难回答,但是“取决于情况”。同样,有时volatile与基础系统上的volatile基本相同,但是JMM并不能真正保证这一点。可以保证写操作对读者是可见的,相对于其他写操作(而不是读操作),写操作是原子的。

最后,您似乎受到性能的激励,但是您应该注意synchronized不是免费的。同样,我要强调的是,它仍然需要同步底层硬件,无论是锁还是volatile,这都是“相对”昂贵的。


或者您可以只使用volatile并获得两全其美:快速读取和快速写入(在某些情况下,您应该考虑AtomicInteger以获得高竞争)。选择确实是您的。