java - 这种创建单例线程的方法安全吗?

标签 java singleton

这里不是讨论Singleton是好是坏。这是关于创建单例。我对单例的理解是,它是一个类,在任何时候都应该存在最多一个对象。也就是说,如果多个类同时实例化一个单例,那么它们将共享该单例的一个实例。
单例的问题在于,一旦它被创建,它将在应用程序的持续时间内存在。使用我的方法,如果不再使用它,您可以随时创建并收集单例垃圾。好吧,我需要一个枚举(Singleton!)来创建所有其他 Singleton。我认为我的方法是反射和序列化安全的,但我不确定线程​​是否有任何问题。
我创建单例的方法如下:

首先任何想成为单例的类都必须扩展下面的类

public abstract class Singleton {
    public Singleton(SingletonFactory.SingletonParam singletonParam) {
        if (singletonParam == null) {
            throw new NullPointerException("singletonParam cannot be null)");
        }
    }

    // For singleton to release resources.    
    public abstract void destroy(SingletonFactory.SingletonParam singletonParam);
}

SingletonParam 将是一个抽象类内部类,它不能在其容器类之外实例化多态意义上的 SingletonParam 对象。
这并不是要让子类在运行时扩展 Singleton 类。它适用于具有静态实例的单例类。我的方法不需要单例类的任何静态实例。
容器是下面的类

注意:在阅读了 Stephen C 的回答后,我进行了更改以从构造函数初始化 HashMap,但我不明白为什么它不是线程安全的。

public enum SingletonFactory {
    INSTANCE;

    enum SingletonList {
       A,
       B,
      ......
    }

   private final HashMap<String, SingletonInfo> mfSingletonInfoHashMap = new HashMap<>();

    // Added after @Stephen C answer
    SingletonFactory() {
        mfSingletonInfoHasmap.put(A, final new SingletonInfo());
     // put all the members of the SingletonList here.   
        At this time the  Singleton member of the SingletonInfo is null.   
        It will be instantiated when a class call getSingleton
     }

    private class SingletonInfo {
        final Set callingObjects = new HashSet();
        Singleton singleton;
    }

   public Object getSingleton(SingletonList inList, Object object) {
      final SingletonInfo singletonInfo = mfSingletonInfoHashMap.get(inList);
       synchronized (singletonInfo) {
              if (singletonInfo.callingObjects.add(object)) {
                  if (singletonInfo.singleton == null) {
                      singletonInfo.singleton = createSingleton(singletonClassName);
                    }
              } else {
                  throw new RuntimeException("getSingleton(" + singletonClassName + ") has already been called and not released");                 
              }
      return singletonInfo.singleton;
  }

    public void releaseSingleton(SingletonList inList, Object object) {
          SingletonInfo singletonInfo = mfSingletonInfoHashMap.get(inList);

          synchronized (singletonInfo) {
            singletonInfo.callingObjects.remove(object);
            if (singletonInfo.callingObjects.isEmpty()) {               
                singletonInfo.singleton.destroy(new SingletonParam() {
            });
            singletonInfo.singleton = null;
          }
      }
  }

    private Singleton createSingleton(SingletonList inList) {
        switch(inList) {
           case SingletonA:
               return new SingletonA(new SingletonParam() {});
           ......
        }
    }

    public abstract class SingletonParam {
        private SingletonParam() {

        }
    }
}

上面的内容不太正确,因为您必须扩展包含 SingletonInfo 中的 CallingObjects 的 HashSet 以通过引用实现相等性,而不是在默认实现中不相等。

由于 SingletonParam 的子类实例无法在 SingletonFactory 之外实例化,因此创建单例对象的唯一方法是调用 SingletonFactory.INSTANCE。 getSingleton(you_singleton_class_name, 这个)

最佳答案

原始版本不是线程安全的。 getSingleton 方法正在读取和更新 HashMap,但没有同步任何内容。


I see there maybe a problem when there is no SingletonInfo associates with a singleton class name yet.

正确。


这是关于代码的更新版本:

In that case I can just populate my hashmap with all the singleton class names with empty CallingObjecs and null Singleton and then synchronize on the SingletonInfo. Does that sound right?

是的,前提是:

  1. 初始化发生在 SingletonFactory 构造函数中,
  2. HashMap 已安全发布,并且
  3. hashmap 在工厂构建完成后没有任何改变。

但是,这似乎会破坏您最初实现的优势之一;即单例工厂不是单例的先验知识。

更好的方法(即保留您最初尝试的解决方案的优势的方法)是同步对 HashMap 的访问,或使用 ConcurrentHashMap .. . 及其特殊的原子操作。


Since I learn programming just by coding and no formal training ...

如果您要学习正确使用 Java 并发,您至少需要阅读一本关于 Java 并发的好教科书。试试 Goetz 等人。

(你需要了解Java并发的原理,你不太可能通过反复试验来学习它们。)

当“公认的智慧”是单例是一个坏主意时,(IMO)您如此专注于使单例设计模式起作用是令人担忧的。您是否考虑过依赖注入(inject)及其优势?

另一件事是这些不是真正的单例。

  • Singleton 类的隐含语义与其名称相矛盾。 Singleton 可以有多个实例 ...

  • 没有什么可以阻止某人创建给定 Singleton 子类的多个实例。鉴于您的实现方法,每个子类都需要有一个非私有(private)构造函数才能使工厂正常工作。

  • 除非您将 Singleton 子类声明为 final,否则会提供另一种打破单例性的途径。

简而言之,我不相信您正在构建的这个基础设施能够更好地实现您实现(真正的)单例的目标。

关于java - 这种创建单例线程的方法安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41134964/

相关文章:

java - 处理类的 OOP 问题

java - 使用 Hadoop 解决大数据问题

java - Java : org. postgresql.util.PSQLException 中的错误:错误:附近的语法错误;

c# - 关于单例的非静态成员的问题(C#)

singleton - 是否有一个意思是 "not a singleton"的名字?

java - 从具有特定类的页面的所有表格内的 <td> 标记中提取数据

java - 我应该使用哪种 Arquillian 配置?

java - 我应该创建许多单例或单例上下文来引用我的状态和对象吗?

ASP.net:单例类,每个请求仅实例化一次?

C# 垃圾收集器似乎过早关闭了我的 StreamWriter