【问题标题】:Are these two examples equivalent (volatile + synchronized increment vs synchronized fetch + increment)?这两个示例是否等效(易失性 + 同步增量与同步提取 + 增量)?
【发布时间】:2018-09-26 02:39:30
【问题描述】:

我研究了synchronized和volatile之间的区别,并写了一个不带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++;
        }
    }
}

没有 Volatile 的版本

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) 线程安全

如果其中一个示例不能保证线程安全,请提供一个说明缺乏线程安全的示例。

【问题讨论】:

  • 一般来说,同步比读取 volatile 更昂贵。您是否只是将其用作示例?因为 AtomicInteger 类可能是您开始时最好的选择(它在幕后使用 Unsafe 类)。

标签: java multithreading locking synchronized volatile


【解决方案1】:

volatile 被认为是一种较弱的同步形式。您使用 volatile 来确保读取时当前值的可见性,因此您对所有可变操作使用锁定,而对只读操作使用 volatile。锁只允许一个线程同时访问一个值,而 volatile 读取允许多个线程,因此当您使用 volatile 保护读取代码路径时,您将获得比对所有代码使用锁定更高程度的共享路径。所以前者的表现优于后者。

【讨论】:

  • 因此两者的功能是相同的(如,两者都是线程安全的),但后者由于不能同时执行多个(安全)提取而遭受性能损失?
【解决方案2】:
  1. 简单地说,第一个对作者来说很慢,对读者来说很快,第二个对两者都平衡(假设)。您应该注意,在某些架构上,volatile 与实现中的同步本质上是相同的,因为底层系统仍然需要保持内存一致性,因此在性能方面使用任何一个示例都可能无关紧要。话虽如此,只是我在空中挥手并假设,您应该在您的目标系统上测试和基准测试并亲自看看。现在测量,以后再优化。

  2. 是的,它是线程安全的。第一个更弱一致,它并不总是保证看到最新的值,因为您可能会在复合更新期间读取,但只要编写者不相互干扰,它并不重要,因为它可以最多只能落后于“实际”值 1。当然,有时强一致性很重要,因此您应该再次考虑这会如何影响您自己的目标系统。

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

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


或者您可以只使用AtomicInteger 并获得两全其美:快速读取和快速写入(除了在某些情况下,您应该查看LongAdder 以获得高争用)。选择真的是你的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-02
    • 2011-07-28
    • 1970-01-01
    • 2020-01-25
    • 2014-12-27
    • 1970-01-01
    • 2011-11-30
    • 1970-01-01
    相关资源
    最近更新 更多