对于Java中线程安全的延迟加载单例来说,这是一个很好的实现吗?

对于Java中线程安全的延迟加载单例来说,这是一个很好的实现吗?,java,multithreading,singleton,lazy-initialization,Java,Multithreading,Singleton,Lazy Initialization,我想在Java中实现线程安全的延迟加载单例 这个设计好吗 public final class ThreadSafeLazySynchronizedSingleton { private ThreadSafeLazySynchronizedSingleton(){} private static ThreadSafeLazySynchronizedSingleton instance; public static ThreadSafeLazySynchronizedSingleton ge

我想在Java中实现线程安全的延迟加载单例

这个设计好吗

public final class ThreadSafeLazySynchronizedSingleton {

private ThreadSafeLazySynchronizedSingleton(){}

private static ThreadSafeLazySynchronizedSingleton instance;

public static ThreadSafeLazySynchronizedSingleton getSynchronizedInstance(){
    synchronized(ThreadSafeLazySynchronizedSingleton.class){ 
        if (instance==null){
            instance = new ThreadSafeLazySynchronizedSingleton();
        }
        return instance;
    }
}

}

我还想创建一个无锁版本,用于一个线程访问Singleton的次数比其他线程多的场景,我如何使用Java中的原子对象实现这一点,ie AtomicBoolean或AtomicReference等。您能提供一个代码示例吗?

最简单的事情可能会像您需要的那样懒惰:

private static ThreadSafeLazySynchronizedSingleton instance
    = new ThreadSafeLazySynchronizedSingleton();

public static ThreadSafeLazySynchronizedSingleton instance() {
    return instance;
}
如果为True,则将在加载类后立即创建实例。但是,在使用该类之前,不会加载该类,在这一点上,无论如何,您可能需要获取实例

如果ThreadSafeLazySynchronizedSingleton类也有不使用实例的静态帮助器方法,但这表明它们与类的其余部分没有太多关系,可能应该移动到它们自己的类中,那么这就不会像您需要的那样懒惰了


很多人也对单例使用单值枚举。就我个人而言,我觉得它很难看,但它确实有一些优点,可以为您处理私有构造函数和实例,而且即使您的类是可序列化的,也可以强制执行单例性,这在其他方面并非微不足道。

最简单的事情可能是您所需要的懒惰:

private static ThreadSafeLazySynchronizedSingleton instance
    = new ThreadSafeLazySynchronizedSingleton();

public static ThreadSafeLazySynchronizedSingleton instance() {
    return instance;
}
如果为True,则将在加载类后立即创建实例。但是,在使用该类之前,不会加载该类,在这一点上,无论如何,您可能需要获取实例

如果ThreadSafeLazySynchronizedSingleton类也有不使用实例的静态帮助器方法,但这表明它们与类的其余部分没有太多关系,可能应该移动到它们自己的类中,那么这就不会像您需要的那样懒惰了


很多人也对单例使用单值枚举。就我个人而言,我觉得它很难看,但它确实有一些优点,可以为您处理私有构造函数和实例,而且即使您的类是可序列化的,也可以强制执行单例性,这在其他方面并不微不足道。

更好的设计会更好

public final class ThreadSafeLazySynchronizedSingleton {

    private ThreadSafeLazySynchronizedSingleton(){}

    private static volatile ThreadSafeLazySynchronizedSingleton instance;

    public static ThreadSafeLazySynchronizedSingleton getSynchronizedInstance(){
        if (instance==null)
            synchronized(ThreadSafeLazySynchronizedSingleton.class){ 
                if (instance==null){
                    instance = new ThreadSafeLazySynchronizedSingleton();
                }
        return instance;
        }
    }
}
然而,IMHO的最佳设计是

public enum ThreadSafeLazySynchronizedSingleton {
    INSTANCE;
}

更好的设计是

public final class ThreadSafeLazySynchronizedSingleton {

    private ThreadSafeLazySynchronizedSingleton(){}

    private static volatile ThreadSafeLazySynchronizedSingleton instance;

    public static ThreadSafeLazySynchronizedSingleton getSynchronizedInstance(){
        if (instance==null)
            synchronized(ThreadSafeLazySynchronizedSingleton.class){ 
                if (instance==null){
                    instance = new ThreadSafeLazySynchronizedSingleton();
                }
        return instance;
        }
    }
}
然而,IMHO的最佳设计是

public enum ThreadSafeLazySynchronizedSingleton {
    INSTANCE;
}

如果您想要一个真正的无锁实现,则需要一些旋转。否则,@yshavit或@peterlawrey是最好的答案

无锁:

private static final AtomicBoolean created = new AtomicBoolean(false);
private static volatile LazySingleton instance = null;

public static ThreadSafeLazySynchronizedSingleton getIntsance(){
   if(instance != null) return instance.get();

   if(created.compareAndSet(false, true)){
      //only one thread can try and create
      instance = new LazySingleton();
   } else {
      //other thread is creating, spin until ready
      while(instance == null);
   }
   return instance;
}

如果您想要一个真正的无锁实现,则需要一些旋转。否则,@yshavit或@peterlawrey是最好的答案

无锁:

private static final AtomicBoolean created = new AtomicBoolean(false);
private static volatile LazySingleton instance = null;

public static ThreadSafeLazySynchronizedSingleton getIntsance(){
   if(instance != null) return instance.get();

   if(created.compareAndSet(false, true)){
      //only one thread can try and create
      instance = new LazySingleton();
   } else {
      //other thread is creating, spin until ready
      while(instance == null);
   }
   return instance;
}

在Java中实现单例设计模式的推荐方法是使用枚举方法。枚举方法非常简单,并且隐式地是线程安全的

public enum EnumTest {
  INSTANCE;

  // Test code
  public static void main(String[] args) {

      // First thread
      Thread t1 = new Thread(new Runnable() {
         @Override
         public void run() {
             EnumTest obj = EnumTest.INSTANCE;
         }   
      });
      t1.start();

      // Second thread
      Thread t2 = new Thread(new Runnable() {
         @Override
         public void run() {
             EnumTest obj = EnumTest.INSTANCE;
         }   
      });  
      t2.start();
   }
 }

很好的教程。

在Java中实现单例设计模式的推荐方法是使用枚举方法。枚举方法非常简单,并且隐式地是线程安全的

public enum EnumTest {
  INSTANCE;

  // Test code
  public static void main(String[] args) {

      // First thread
      Thread t1 = new Thread(new Runnable() {
         @Override
         public void run() {
             EnumTest obj = EnumTest.INSTANCE;
         }   
      });
      t1.start();

      // Second thread
      Thread t2 = new Thread(new Runnable() {
         @Override
         public void run() {
             EnumTest obj = EnumTest.INSTANCE;
         }   
      });  
      t2.start();
   }
 }

很好的教程。

您还可以同步整个getSynchronizedInstance方法,而不是自己显式地获取锁@PeterLawrey更不用说它是实现单例模式的推荐方法了!您还可以使整个getSynchronizedInstance方法同步,而不是自己显式地获取锁@PeterLawrey更不用说它是实现单例模式的推荐方法了!它如何发送不同的参考资料?只有一个将被创建和分配。我选择不使用AtomicReference,大概是因为它懒得创建多个。使用AtomicReference,您可以创建多个ie:1。创建实例2。尝试在原子引用中设置该实例。只能创建一个实例,但这是不安全的。假设有人在CAS之后但在创建之前进来。settrue。此时,由于CAS,created.get==true,因此第二个调用方返回实例。但是他们在created.settrue之前就这样做了。settrue提供了一个相对于created.get的HB边,因此在return实例和instance=new LazySingleton之间没有可传递的HB。这意味着第二个线程仍然可以看到null或部分构造的对象。它仍然在那里,在第一行。Btw已创建。获取!=你什么意思?它不会编译created.get返回布尔值。我认为你需要完全摆脱第一行,然后做CAS else旋转。另外,最后一行需要是return instance.get,如果需要,您还可以使用CAS或spin中的一个本地引用。您是对的,我没有仔细阅读第一个版本。它如何发送不同的引用?只有一个将被创建和分配。我选择不使用AtomicReference,大概是因为它懒得创建多个。使用AtomicReference,您可以创建更多

n:1。创建实例2。尝试在原子引用中设置该实例。只能创建一个实例,但这是不安全的。假设有人在CAS之后但在创建之前进来。settrue。此时,由于CAS,created.get==true,因此第二个调用方返回实例。但是他们在created.settrue之前就这样做了。settrue提供了一个相对于created.get的HB边,因此在return实例和instance=new LazySingleton之间没有可传递的HB。这意味着第二个线程仍然可以看到null或部分构造的对象。它仍然在那里,在第一行。Btw已创建。获取!=你什么意思?它不会编译created.get返回布尔值。我认为你需要完全摆脱第一行,然后做CAS else旋转。另外,最后一行需要是return instance.get,如果需要,还可以使用CAS或spin中的一个本地引用,我没有仔细阅读第一个版本。我认为同步块中的变量是同步的,所以在退出时将更改发布到共享内存,在进入时读取最新版本,所以在我的实现中不需要volatile关键字?非决赛也可以,因为它是私人的?您的实现不应该需要同步块或易失性变量吗?@aranhakki哦,您是对的-您拥有的根本不是双重检查锁。我误解了这个问题。但是我的实现不需要synchronized/volatile,因为final修饰符也提供了一些有限制的before保证,但对于这个用途来说已经足够了。啊,好吧,我不知道final关键字,请投票给我,让我发现你的错误,我可以用分数来做!我认为同步块中的变量是为您同步的,所以在退出时将更改发布到共享内存,在进入时读取最新版本,所以在我的实现中不需要volatile关键字?非决赛也可以,因为它是私人的?您的实现不应该需要同步块或易失性变量吗?@aranhakki哦,您是对的-您拥有的根本不是双重检查锁。我误解了这个问题。但是我的实现不需要synchronized/volatile,因为final修饰符也提供了一些有限制的before保证,但对于这个用途来说已经足够了。啊,好吧,我不知道final关键字,请投票给我,让我发现你的错误,我可以用分数来做!enum是如何在后台工作的,这样它们就可以作为单例使用?@aranhakki An enum通过设计列出了所有可能的实例。如果指定一个实例,则有一个单例。它是线程安全的,类是惰性加载的。为什么要检查实例是否为null两次?@EricGnulver,因为当前线程获取锁时,另一个线程可能已经初始化了singleton。因此,如果获取锁的线程遇到这种情况,它将接收与另一个线程初始化的实例相同的实例?枚举如何在引擎盖下工作以使它们可以用作单例?@aranhakki一个枚举按设计列出所有可能的实例。如果指定一个实例,则有一个单例。它是线程安全的,类是惰性加载的。为什么要检查实例是否为null两次?@EricGnulver,因为当前线程获取锁时,另一个线程可能已经初始化了singleton。因此,如果获取锁的线程遇到这种情况,它将接收另一个线程初始化的相同实例?