当前位置: 首页 > 面试题库 >

挥发性出版物的保证深度如何?

袁鹤轩
2023-03-14
问题内容

众所周知,如果我们有一些对象引用并且此引用具有final字段,则将确保-我们将看到final字段中的所有可访问字段(至少在构造函数完成时)

范例1:

class Foo{
    private final Map map;
     Foo(){
         map = new HashMap();
         map.put(1,"object");
     }

     public void bar(){
       System.out.println(map.get(1));
     }
}

正如我在这种情况下所理解的那样,我们保证该bar()方法总是输出,object因为:
1.我列出了class Foo和map的完整代码是最终的;
2.如果某个线程将看到的引用,Foo而该引用!= null,则我们保证从最终map引用值可达到将是实际的

我也认为

范例2:

class Foo {
    private final Map map;
    private Map nonFinalMap;

    Foo() {
        nonFinalMap = new HashMap();
        nonFinalMap.put(2, "ololo");
        map = new HashMap();
        map.put(1, "object");
    }

    public void bar() {
        System.out.println(map.get(1));
    }

    public void bar2() {
        System.out.println(nonFinalMap.get(2));
    }
}

在这里,我们对bar()方法有相同的保证,但是尽管在赋值之前发生
赋值,但还是bar2可以抛出。NullPointerException``nonFinalMap``map

我想知道volatile:

范例3:

class Foo{
        private volatile Map map;
         Foo(){
             map = new HashMap();
             map.put(1,"object");
         }

         public void bar(){
           System.out.println(map.get(1));
         }
    }

据我了解,bar()方法不能抛出NullPoinerException但可以打印null;(我完全不确定这方面)

范例4:

class Foo {
    private volatile Map map;
    private Map nonVolatileMap;

    Foo() {
        nonVolatileMap= new HashMap();
        nonVolatileMap.put(2, "ololo");
        map = new HashMap();
        map.put(1, "object");
    }

    public void bar() {
        System.out.println(map.get(1));
    }

    public void bar2() {
        System.out.println(nonFinalMap.get(2));
    }
}

我认为,我们在这里有大约相同的注册就bar()方法也bar2()不能扔NullPointerException,因为nonVolatileMap转让的书面较高的挥发性映射分配,但它可以输出空

*在Elliott Frisch评论后 *添加

通过竞赛示例发布:

public class Main {
    private static Foo foo;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                foo = new Foo();
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                while (foo == null) ; // empty loop

                foo.bar();
            }
        }).start();

    }
}

请证明或更正我对代码段的注释。


问题答案:

在当前的Javahtml" target="_blank">html" target="_blank">内存模型领域中,volatile不等于final。换句话说,您不能将替换finalvolatile,并认为安全构造保证是相同的。值得注意的是,这在理论上可能会发生:

public class M {
  volatile int x;
  M(int v) { this.x = v; }
  int x() { return x; }
}

// thread 1
m = new M(42);

// thread 2
M lm;
while ((lm = m) == null); // wait for it
print(lm.x()); // allowed to print "0"

因此,volatile在构造函数中编写字段并不安全。

直觉:m上面的例子中有一场竞赛。创造 场地 并不能消除种族M.x
volatile,只有制造m自己volatile会有所帮助。换句话说,volatile该示例中的修饰符在 错误的位置
可用。在安全发布中,您必须具有“写入->易失性写入->易失性读取,可观察到易失性写入->读取(现在先观察易失性写入之前的写入)”,而必须具有“易失性写入->写入->读取-

易失性读取(不遵循易失性写入)”。

琐事1:
此属性表示我们可以volatile在构造函数中更加积极地进行优化。这证实了this可以放松未观察到的易失性存储的直觉(实际上,直到具有非转义完成的构造函数才观察到)。

琐事2:
这也意味着您不能安全地初始化volatile变量。在上面的示例中替换MAtomicInteger,您将具有独特的现实生活行为!调用new AtomicInteger(42)一个线程,不安全地发布该实例,然后get()在另一个线程中执行-
您是否保证遵守42?如前所述,JMM说“不”。Java内存模型的最新修订版努力确保所有初始化的安全构造,以捕获这种情况。许多重要的非x86端口已经加强了它的安全性。

Trivia 3: Doug Lea:“ finalvs
volatile问题导致在java.util.concurrent中进行了一些曲折的构造,以允许在自然不存在的情况下将0用作基本/默认值。该规则很糟糕,应该更改。”

也就是说,可以使示例更加狡猾:

public class C {
  int v;
  C(int v) { this.x = v; }
  int x() { return x; }    
}

public class M {
  volatile C c;
  M(int v) { this.c = new C(v); }
  int x() { 
    while (c == null); // wait!
    return c.x();
  }
}

// thread 1
m = new M(42);

// thread 2
M lm;
while ((lm = m) == null); // wait for it
print(lm.x()); // always prints "42"

如果 volatile读取观察到volatile写入构造函数中写入的值 之后
存在一个传递性直读volatile字段,则通常的安全发布规则开始生效。 __



 类似资料:
  • 我正在学习著名的《关于Java》一书。这本书说:“如果一个线程写入一个变量,而其他线程只读取它,那么你可以让该变量保持易失性。”我不明白为什么不需要“易失性”来确保将线程刷新变量写回内存?如果没有易失性,其他线程是否会因为本地缓存而读取脏数据?

  • 问题内容: JVM是否保证不缓存非可变变量? 程序员是否可以依赖JVM始终为每个线程本地缓存非易失性变量。 或JVM可能会或可能不会这样做,因此程序员不应为此而依赖JVM。 预先感谢您的回答。 问题答案: 否。JVM不能保证“缓存”非易失性字段。JVM保证的实现是 可变字段应如何表现 。字段的缓存是非标准的(未指定),并且因JVM的实现而异。因此,您不应该真正依赖它(即使通过某种方式发现某些数据正

  • 问题内容: 我有一个缓存类,其中包含一个存储缓存项。 我很好奇更改为会带来什么后果? 我会提高性能吗?此缓存是只读缓存。 最佳选择是什么?只是HashMap?缓存将按一定间隔进行填充。 问题答案: 首先,您似乎不了解关键字的作用。它确保如果声明的变量保留的 引用值发生更改,则其他线程将看到它,而不是拥有缓存的副本。它与访问线程安全无关。 鉴于此,并且您说的是只读事实,您当然不需要使用任何提供线程安

  • Perl 语言编程,第四版 称为大骆驼书,真正的 Perl 圣经。 Perl 语言入门,第六版 称为小骆驼书,Perl 标准教程。 Perl Cookbook,第二版 一本如何利用 Perl 干活的问题导向书籍。 Object Oriented Perl 关于 Perl 5 面向对象编程的最佳解释。 Perl 最佳实践 展示如何写优良、可维护的 Perl 代码。 The Perl Review 在

  • 我目前添加了一个CSRF令牌保护机制到我的php应用程序。正如我所读到的,唯一的要求是一个独特的每个用户令牌,我在php7中使用random_bytes生成。 我担心的是,如果攻击者使用用户的浏览器发送http请求,浏览器不会发送令牌的会话变量吗?(因为用户具有与令牌关联的sessionid)。 我将令牌存储在会话变量的一个隐藏值内。 例如:我的令牌存储在会话变量中,然后攻击者将我发送到具有csr

  • 问题内容: 这个问题与尚未使用的intl-tel-input和vuejs2一起使用相似。 因此,长话短说,我正在动态设置一个新的引导程序选项卡(标题和URL),然后尝试使用来(重新)绑定某些功能。 在我的Vue方法中添加以下行https://github.com/thecodeassassin/bootstrap-remote-data/blob/master/js/bootstrap-remot