【问题标题】:Volatility and thread safety in JavaJava中的易变性和线程安全
【发布时间】:2013-07-06 02:19:55
【问题描述】:

请举这个非常简单的例子:

volatile int globalVar = 1;

我的话题:

if (globalVar > 0) {
    globalVar--;
}

现在,我必须决定使变量 volatile 是否足以防止竞争条件并使该线程安全。

我知道递增和递减不是,但我不确定它是否不同,因为前面的条件。

我觉得还是不安全,因为可以按这个顺序执行:

线程 A 检查条件。线程 B 检查条件。 线程 A 递增。线程 B 递增。

我说的对吗?

【问题讨论】:

  • 这不是唯一的原因;单独递减不是线程安全的。
  • 你需要把globalVar--想象成线程加载globalVar,从结果中减去1,然后将新值写入globalVar,三个独立的操作。

标签: java multithreading thread-safety race-condition volatile


【解决方案1】:

volatile 不保证原子性。你可以简单地添加一个同步块。

synchronized(this)
{
 if (globalVar > 0) {
   globalVar--;
 }
}

【讨论】:

    【解决方案2】:

    为了使您的代码线程安全,您需要在检查和修改周围添加一个synchronized 块。

    可以做的一个例子:

    volatile Integer globalVar = 1;
    

    然后在你可以做的工作的方法中:

    synchronized public void decrement(){
        if(globalVar > 0)
              globalVar--;
        }
    }
    

    【讨论】:

    • 这行不通。 globalVar 是不可变的,因此对其进行修改将创建一个新对象,这意味着不同的线程将锁定不同的对象。这就是为什么您通常应该在最终引用上同步的原因。
    【解决方案3】:

    不,它不是线程安全的。 Volatile 不保证原子性。像这样的操作 i-- 不是原子的,因此将 i 声明为 volatile 没有帮助。

    int globalVar = 1;
    
    lock.lock()
    try{
      if(globalVar > 0)
              globalVar--;
    
    }finally {
       lock.unlock();
    }
    

    上面的代码可以工作,你不需要在这里将 gobalVar 声明为 volatile 因为
    Volatile 提供两个保证: 1. 能见度 2.重新排序

    同步/加锁/解锁三包 1.原子性 2. 能见度 3.重新排序

    还要记住,当一个对象在多个线程之间共享时,总是访问该对象 任何同步动作下的对象同步/易失性读/写。

    在此处阅读 JLS 作者第 17 章的一篇优秀文章。必读 http://jeremymanson.blogspot.in/2008/11/what-volatile-means-in-java.html http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

    【讨论】:

      【解决方案4】:

      你是对的。由于您声明的原因,它不是线程安全的。 volatile 确保两个线程都能看到变量的更新值,但不保护多行代码。

      【讨论】:

      • 它甚至不保护单行代码 - globalVar-- 不是原子的。
      • OP 强调的内容:I know incrementing and decrementing is not
      猜你喜欢
      • 2015-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多