在哪些情况下,易失性比锁定更好?

时间:2017-10-13 13:48:53

标签: java multithreading

The Java Language Specification, Java SE 9 Edition中有一个声明:

  

Java编程语言允许线程访问共享变量(§17.1)。通常,为了确保共享变量的一致性和可靠性更新,线程应确保通过获取锁定来独占使用此类变量,通常会对这些共享变量实施互斥。

     

Java编程语言提供了第二种机制volatile字段,比某些用途的锁定更方便。

我试图找出使用volatile的目的比锁定更方便?

4 个答案:

答案 0 :(得分:2)

  

我正在试图弄清楚使用volatile的目的是什么?更方便而不是锁定?

在简单的情况下,这是一个更方便的 1 解决方案,其中volatile是唯一需要同步的状态,并且操作本质上是原子的。

例如:

public class Processor extends Thread {
    private volatile stopped;

    public void run() {
        while (!stopped) {
            // do stuff
        }
    }

    public void stop() {
        stopped = true;
    }
}

public class Test {
    public static void main(String[] args) {
        Processor p = new Processor();
        p.start();
        Thread.sleep(1000);
        p.stop();
    }
}

如果对stopped使用常规变量,则在阅读和编写时需要使用synchronized(或其他形式或锁定)...这样代码更多,不太方便。< / p>

 public class Processor extends Thread {
    private stopped;

    public void run() {
        while (!isStopped()) {
            // do stuff
        }
    }

    public synchronized void stop() {
        stopped = true;
    }

    private synchronized boolean isStopped() {
        return stopped;
    }
}

但是,这仅适用于在简单情况下锁定的替代方法。如果受保护状态需要的不仅仅是原子读取和原子写入,那么它将无法工作。例如:

public class Counter {
    private volatile int counter;

    public void increment() {
        counter = counter + 1;
    }

    private int get() {
        return counter;
    }
}

由于increment()的实现方式,上述内容不是线程安全的。为了正确(线程安全)行为,increment = increment + 1需要以原子方式完成。 volatile不提供该保证。内存readwrite操作都是原子的,但它们不是原子序列。

AFAIK,使用 Counter变量实现volatile没有线程安全的方法。您需要锁定或AtomicInteger(通常依赖于CAS硬件支持......)

1 - 如果只计算代码行,会更方便。如果你也考虑到代码作者和维护者在推理上花费的工作,解决方案是正确的,我会认为“便利”在很大程度上是一种错觉。

  

在哪些情况下,易失性比锁定更好?

这是一个更难的问题,因为“更好”是一个加载的术语。但如果你的意思是“性能更高”,那么volatile工作的情况下通常会更好:

  • volatile读取或写入只会导致内存障碍。
  • 锁定操作通常使用CAS指令和一些代码来实现,以处理争用。 CAS引入了内存屏障。
  • Atomic类型通常也使用CAS实现。

这种分析有点粗糙,但最底层的是,如果volatile能够胜任这项工作,那么它可能比替代方案更有效率。但有两点需要注意:

  • 涉及线程间通信或同步的大多数操作对于volatile
  • 而言过于复杂
  • 在正常情况下,volatile的性能优势太小而不显着。如果存在争用或同步瓶颈,则会发生更改。

答案 1 :(得分:1)

要完成Stephen C的答案,volatile关键字提供共享变量的原子读取和写入,然后在JVM内部不使用与synchronized块相反的任何监视器。

因此,对于共享的简单变量,从性能的角度来看,它应该更有效。

但正如已经提到的那样,它仅适用于简单的变量访问。

请参阅文档here

答案 2 :(得分:1)

  • 为什么 volatile :它不涉及上下文切换或同步开销。写入总是将状态刷新到主存储器。

  • volatile 时:通常用于布尔变量,如果没有复合语句。即操作是原子的。 i++不是原子的。

  • 替代方案:使用CAS语句或锁定的原子变量。

答案 3 :(得分:1)

如果您有单个编写者线程和多个读者线程,则可以使用volatile

但如果你有多个作家和读者线程volatile不提供数据一致性。

更多细节可以阅读@ Difference between volatile and synchronized in Java