摘自《 Java并发实践》第26页:

仅当满足以下所有条件时,才可以使用 volatile 变量:

  • 写入变量不取决于其当前值,或者您可以确保仅单个线程更新该值;
  • 变量不参与其他状态变量的不变量;和
  • 无需访问
  • 进行其他任何原因的锁定,就可以访问该变量。

  • 如何理解“使用volatile关键字时,变量不与其他状态变量一起参与不变式”?

    最佳答案

    “不变式”的简单定义:在对象生存期内始终为真的条件。



    这就是为什么您不能在具有关联多个变量的不变量的类中使用它们的原因。

    例如,假设您有一个class来建模一个由两个变量描述的时间间隔:startend。不变条件可能是start始终小于或等于end。如果将两个变量(仅作为示例)都声明为volatile,则可以依赖volatile的可见性功能,但不能确保在涉及两个变量的更改期间始终满足不变量。思考:

    public void setInterval(Date newStart, Date newEnd)
    {
     // Check if inputs are correct
    
     // Here the object state is valid
     start = newStart;
    
     // If another thread accesses this object now it will
     // see an invalid state because start could be greater than end
    
     end = newEnd;
     // Here the object state is valid again
    }
    

    在这种情况下,您可以确保所做的更改对每个线程都是可见的,但是在两条指令的中间,对象状态可能无效。因为它可以被其他线程访问(请记住,这是一个简单的情况,所以有可能,但不太可能),因此不变条件“start
    这就是为什么不鼓励在(一组)定义良好的模式之外使用volatile的原因。仅在满足以下条件时才应使用volatile变量:
  • 该变量不涉及与其他变量相关的不变量(出于上述原因)。
  • 要写入变量的值不取决于其当前值。

  • 例如,表达式int a = i++;不是原子的,那么严格来说不是线程安全的,因为它将被如下所示的代码重写:
    int temp = i;
    i = i + 1;
    int a = temp;
    

    从线程的角度来看,要使其具有原子性,您可以想象一个像这样的类:
    public class MyAtomicInteger
    {
      public synchronized increment()
      {
        x = x + 1;
      }
    
      private int x;
    }
    

    当然,它确实存在此AtomicInteger的实现,并且是java.util.concurrent.atomic包的一部分,它为无锁并发编程提供了一些简单的基本例程。

    关于java - 如何理解 "The variable does not participate in invariants with other state variables when using volatile keyword"?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9868577/

    10-11 22:20
    查看更多