在java中使用volatile的单例

Try*_*ing 14 java concurrency

class MyClass
{
      private static volatile Resource resource;

      public static Resource getInstance()
      {
            if(resource == null)
                  resource = new Resource();
            return resource;
      }
 }
Run Code Online (Sandbox Code Playgroud)

这里我怀疑是在实践中根据java并发性,如果你使用volatile,安全发布会发生(即一旦引用对另一个线程可见,数据也可用).我可以在这里使用吗?但如果它是正确的,那么假设thread1现在检查"资源"并且它为null,因此它开始创建对象.当thread1正在创建objet时,另一个线程即thread2开始检查"resource"的值,而thread2发现它为null(假设创建"resource"对象需要相当长的时间,而且thread1尚未完成创建,所以安全发布没有发生因此不可用于thread2)那么它还会开始创建对象吗?如果是,那么类不变量就会中断.我对么?请帮我理解这里特别使用的volatile.

cor*_*iKa 18

你是对的,多个线程可以尝试创建一个Resource对象.Volatile只保证如果一个线程更新引用,所有其他线程将看到新引用,而不是一些缓存引用.这比较慢,但更安全.

如果只需要一个延迟加载的资源,则需要执行以下操作:

class MyClass
{
      private static volatile Resource resource;
      private static final Object LOCK = new Object();

      public static Resource getInstance()
      {
            if(resource == null) { 
                synchronized(LOCK) { // Add a synch block
                    if(resource == null) { // verify some other synch block didn't
                                           // write a resource yet...
                        resource = new Resource();
                    }
                }
            }
            return resource;
      }
 }
Run Code Online (Sandbox Code Playgroud)

  • 或者你可以使用`enum`并避免所有这些.http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java (4认同)

小智 14

volatile解决了一个可见性问题.如果要写入一个声明为volatile的变量,则该值将立即显示给其他线程.众所周知,我们在os L1,L2,L3中有不同级别的缓存,如果我们在一个线程中写入变量,则不保证其他人可见,因此如果我们使用volatile,它会写入直接内存并且可见给别人 但挥发性并不能解决原子性问题,即int a; a++;不安全.因为有三个与之相关的机器指令.


Joh*_*int 7

我知道你不是在问更好的解决方案,但如果你正在寻找一个懒惰的单一解决方案,这绝对是值得的.

使用私有静态类来加载单例.在调用之前不会加载类,因此在类之前不会加载引用.通过实现加载类是线程安全的,你也可以产生很少的开销(如果你正在做重复的易失性负载[可能仍然很便宜],这个分辨率总是在初始构造后正常加载).

class MyClass {
    public static Resource getInstance() {
        return ResourceLoader.RESOURCE;
    }

    private static final class ResourceLoader {
        private static final Resource RESOURCE = new Resource();
    }
}
Run Code Online (Sandbox Code Playgroud)