Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/codeigniter/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 引用类型的Volatile-它是否总是避免由于JMM而发布引用问题?_Java_Multithreading_Concurrency_Thread Safety_Volatile - Fatal编程技术网

Java 引用类型的Volatile-它是否总是避免由于JMM而发布引用问题?

Java 引用类型的Volatile-它是否总是避免由于JMM而发布引用问题?,java,multithreading,concurrency,thread-safety,volatile,Java,Multithreading,Concurrency,Thread Safety,Volatile,假设这一类: public class AmIThreadSafe { private int a; private int b; AmIThreadSafe(int a, int b) { this.a = a; this.b = b; } } 假设只要this(引用)转义,一些线程就可以访问该实例对此类的引用(声明为volatile)(导致争用条件): 在这里,我确信分配实例引用的事实发生在线程读取之前 但是AmIThre

假设这一类:

public class AmIThreadSafe {

    private int a;
    private int b;

    AmIThreadSafe(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
假设只要
this
(引用)转义,一些线程就可以访问该实例对此类的引用(声明为
volatile
)(导致争用条件):

在这里,我确信分配
实例
引用的事实发生在线程读取之前

但是
AmIThreadSafe的
字段呢

外部
volatile
关键字是否也意味着
发生在
a
b
字段的关系之前? 或者,由于构造函数期间可能的语句重新排序,是否有可能最终导致任何线程看到过时的值(在本例中是默认值
0
,因为
int

换句话说,我应该声明
a
b
final
还是
volatile
,以防止JMM出现任何意外情况,还是仅仅在实例的引用上指示
volatile

----------------更新帖子-一个好答案:----------------------------

下面的文章通过它的示例证实,在我的例子中,
a
b
受到JMM优化的保护,JMM优化可以防止永久的发生在关系之前


这还不足以让它变得不稳定。线程安全性取决于使用情况。例如,如果另一个线程正在修改值,这仍然可能产生意外的结果

为简单起见,假设
public
变量


volatile
仅适用于变量(例如,
x
y
不会自动
volatile
仅仅因为
实例
是可变的)。这一点应该从中明确,您的案例中的
volatile
仅适用于
AmlThreadSafe
的引用。您仍然必须使实例变量(
a
b
volatile
或在
同步
块中访问它们。否则,您可能会得到过时的数据。

如果
a
b
仅在构造函数中修改,那么在这种情况下,您应该没有问题,因为对象是在引用分配给
实例之前创建的(以及
a
b
集),任何其他线程在这些位置都不会有本地缓存的内存副本,因为它是线程以前无法看到的新对象。换句话说,我认为另一个线程不可能看到
a
b
的“默认”值0,因为构造函数将在对象的引用分配给
实例之前完全运行

但是,如果可以在构造函数之后修改
a
b
,则此处的其他答案是正确的-您需要围绕它们进行同步


如果你要假设
a
b
不会在构造函数之外被修改,那么没有理由不将它们设置为最终值,只是为了安全。

实例
声明为
volatile
不会使其字段
volatile
,但是如果我正确理解你的问题,那么-是的,对你来说就足够了

Per:

  • 一个线程中的
    volatile
    写入发生在另一个线程中的任何后续
    volatile
    读取之前
  • 同一线程中的语句具有预期的“发生在之前”关系
  • 发生在关系可传递之前
因此,如果一个线程认为
实例
已经初始化,那么
实例
的初始化就发生在它之前,而
实例
字段的初始化就发生在它之前,因此线程会认为
实例
的字段已经初始化。

thread 1                 thread 2

1 write(a)

2 write(instance)

                         3 read(instance)

                         4 read(a)
由于实例是可变的,[2]发生在[3]之前

由于before是可传递的,我们有hb(1,2),hb(2,3),hb(3,4),因此hb(1,4)

示例:

class Something{
  private volatile static Something instance = null;
  private int x;
  private int y;
  private Something(){
    this.x = 1;
    this.y = 2;
  }
  public static Something getInstance() {
    if (instance == null) {
        synchronized (Something.class) {
            if (instance == null)
                instance = new Something();
            }
         }  
     }
   return instance; 
  }

}
说明:


假设我们有上面的代码:

现在让我们假设实例在一段时间内不易波动:

线程#1:
在invoke getInstance方法中,检查实例值{since null},将进入IF条件,访问锁现在再次找到实例==null,调用某个构造函数。现在进入构造函数体内部

一旦线程#1进入构造函数体,就会发生上下文切换,现在轮到线程#2执行

线程#2:
调用get Instance,但突然发现该实例不是null?为什么{Reason将在此之后讨论}并因此将部分构造的对象分配给引用并返回它

现在的情况是这样的:线程#1仍然需要完全构造对象{需要完全构造它},线程#2获得了部分构造对象的引用,如果它像say reference那样使用它,x//将打印“x”默认值而不是“1”

为什么在线程#2的情况下返回部分构造的对象引用? 原因很简单:语句重新排序。 对象创建和引用关联的步骤很简单:

  • 在堆中分配内存
  • 执行将初始化类成员的构造函数体
  • 完成上述步骤后,引用新创建的对象
  • 但有时编译器会无序执行这些指令,这意味着:
    可能会发生这样的事情:

  • 在堆中分配内存
  • 对新创建的对象的引用
  • 执行将初始化类成员的构造函数体
  • 一旦发生上述两个步骤,如果发生上下文切换
    thread 1                 thread 2
    
    1 write(a)
    
    2 write(instance)
    
                             3 read(instance)
    
                             4 read(a)
    
    class Something{
      private volatile static Something instance = null;
      private int x;
      private int y;
      private Something(){
        this.x = 1;
        this.y = 2;
      }
      public static Something getInstance() {
        if (instance == null) {
            synchronized (Something.class) {
                if (instance == null)
                    instance = new Something();
                }
             }  
         }
       return instance; 
      }
    
    }