即使只有一个编写器线程,volatile还是有问题的

时间:2019-04-10 05:20:51

标签: java multithreading concurrency volatile

如果只有一个写程序线程和一个读程序线程,我们仍然可以使用带有volatile变量的竞争条件。 就像下面的代码一样,在一种情况下,写程序线程将x的值检查为零是没有任何上下文切换发生的,而读线程也将x的值视为0。 在一种情况下,编写器线程将x的值检查为零(将x递增1)将其刷新到主内存中(因为x是易失的),发生上下文切换,而读取器线程将x的值视为1。 我只是想知道在这种用例中volatile是否足够,还是我们需要进行同步以避免竞争条件

我尝试执行以下代码,但始终能得到相同的结果

public class VolatileSingleWriterClarity {
    static volatile int x = 0;

    public static void main(String[] args) {
        new Thread(VolatileSingleWriterClarity::writer).start();
        new Thread(VolatileSingleWriterClarity::reader).start();
    }

    public static void reader() {
        System.out.println(x);
    }

    public static void writer() {
        x++;
    }
}

1 个答案:

答案 0 :(得分:1)

  

就像下面的代码一样,在一种情况下,写程序线程将x的值检查为零可能不会发生上下文切换,而读线程也将x的值视为0。

是的,那绝对是可能的。即使x是易失性的,x++涉及两个完全独立的操作:易失性读取,然后是易失性写入。这些操作都是原子操作,但是在它们之间进行上下文切换绝对是可能的。 (两个线程也可以在完全独立的处理器内核上运行,因此即使进行上下文切换,读者也有可能在写入者即将写入新值的同时读取原始值。 )


  

我只是想知道在这种用例中,volatile是否足够?还是需要进行同步处理以避免竞争状况

synchronized并不能真正帮助您。您似乎担心要避免这种情况(volatile允许但synchronized可以阻止):

  • writer线程读取x的原始值
  • 阅读器线程读取x的原始值,并将其打印出来
  • writer线程写入x的新值,该值永远不会被读取

但它完全等同于这种情况(volatilesynchronized都允许):

  • 阅读器线程读取x的原始值,并将其打印出来
  • writer线程读取x的原始值
  • writer线程写入x的新值,该值永远不会被读取

因此没有固定另一个就没有意义。

如果您要确保读取器线程在写入器线程写入之后才读取x,则需要做一些更复杂的事情,例如使用waitnotify