java中的Singleton类

时间:2010-01-21 18:23:24

标签: java singleton

我正在考虑编写单例类的其他方法。那么这个类被视为单身类吗?

      public class MyClass{
            static Myclass myclass;

            static { myclass = new MyClass();}

            private MyClass(){}

            public static MyClass getInstance()
            { 
                return myclass;
            }
       }

因为静态块只运行一次。

10 个答案:

答案 0 :(得分:17)

不,不是。您没有声明myClass private static finalgetInstance()也不是static。代码也没有真正编译。

这是Singleton成语:

public class MyClass {
    private static final MyClass myClass = new MyClass();

    private MyClass() {}

    public static MyClass getInstance() {
        return myClass; 
    }
}

它应该是private,以便其他人无法直接访问它。它应该是static,因此只有一个。应为final,以便无法重新分配。您还需要在声明期间直接将其实例化 ,这样您就不必担心(那么多)线程。

如果加载很昂贵而你更喜欢延迟加载Singleton,那么考虑Singleton holder成语,它按需进行初始化而不是在类加载期间:

public class MyClass {
    private MyClass() {}

    private static class LazyHolder {
        private static final MyClass myClass = new MyClass();
    }

    public static MyClass getInstance() {
        return LazyHolder.myClass;
    }
}

但是,无论是否需要 Singleton ,都应该提出大问号。通常不需要它。 只需静态变量,枚举,工厂类和/或依赖注入通常是更好的选择。

答案 1 :(得分:14)

这是另一种方法:

public enum Singleton{
  INSTANCE("xyz", 123);

  // Attributes
  private String str;
  private int i;

  // Constructor
  Singleton(String str, int i){
    this.str = str;
    this.i = i;
  }
}

根据Josh Bloch的Effective Java,这是在Java中实现Singleton的最佳方法。与涉及私有静态实例字段的实现不同,私有静态实例字段可以通过滥用反射和/或序列化进行多次实例化,枚举可以保证是单例。

枚举单例的主要限制是它们总是在类加载时实例化,不能懒惰地实例化。因此,例如,如果要使用运行时参数实例化单例,则必须使用不同的实现(最好使用双重检查锁定)。

答案 2 :(得分:2)

我是这样做的。它更快,因为它在创建实例时只需要synchronized块。

public class MyClass
{
    private static MyClass INSTANCE=null;

    private MyClass()
    {
    }

    public static MyClass getInstance()
    {
        if(INSTANCE==null)
        {
            synchronized(MyClass.class)
            {
                if(INSATCNE==null) INSTANCE=new MyClass();
            }
        }
        return INSTANCE;
    }
}

答案 3 :(得分:1)

使用您的示例并使用GoF的实现方式:

public class MyClass{
    private static Myclass instance;

    private MyClass(){
        //Private instantiation
    }

    public static synchronized MyClass getInstance()  //If you want your method thread safe...
    { 
        if (instance == null) {
            instance = new MyClass();
        }

        return instance;
    }
}

希望这会有所帮助:

答案 4 :(得分:1)

您的课程(原始代码,编辑前):

public class MyClass {
    Myclass myclass;

    static { myclass = new MyClass();}

    private MyClass(){}

    public MyClass getInstance()
    {
        return myclass;
    }
}

不是真正的单身人士:

  1. 字段myclass不是私有的,可以从外部读取和更改(假设你有一个实现它的实例
  2. 字段myclass不是静态的,无法在静态构造函数中访问(编译错误)
  3. getInstance()方法不是静态的,因此您需要一个实例来调用它

  4. 实际代码:

    public class MyClass {
        static Myclass myclass;
    
        static { myclass = new MyClass();}
    
        private MyClass(){}
    
        public static MyClass getInstance()
        {
            return myclass;
        }
    }
    

    仍然myclass不是私有的(也不是最终的)......声明它是最终的将有助于防止无意中从班级内部改变。

    private static final Myclass myclass;
    

答案 5 :(得分:1)

有三种方法可以在java中创建单例。

  1. 急切初始化单身人士

    public class Test {
        private static final Test test = new Test();
    
        private Test() {
        }
    
        public static Test getTest() {
            return test;
        }
    }
    
  2. 延迟初始化单例(线程安全)

    public class Test {
        private static volatile Test test;
        private Test(){}
        public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();}
                }
            }
            return test;
        }
    }
    
  3. Bill Pugh Singleton with Holder Pattern(最好是最好的一个)

    public class Test {
        private Test(){}
    
        private static class TestHolder {
            private static final Test test = new Test();
        }
    
        public static Test getInstance() {
            return TestHolder.test;
        }
    }
    

答案 6 :(得分:1)

public class singletonPattern {
    private static singletonPattern obj;

    public static singletonPattern getObject() {
        return obj = (obj == null) ? new singletonPattern() : obj;
    }

    public static void main(String args[]) {
        singletonPattern sng = singletonPattern.getObject();
    }
}

答案 7 :(得分:0)

在这个游戏上可能会有点迟到,但基本的实现看起来像这样:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

这里我们有MySingleton类,它有一个名为INSTANCE的私有静态成员,以及一个名为getInstance()的公共静态方法。第一次调用getInstance()时,INSTANCE成员为null。然后,流将进入创建条件并创建MySingleton类的新实例。对getInstance()的后续调用将发现已经设置了INSTANCE变量,因此不会创建另一个MySingleton实例。这确保了只有一个MySingleton实例在getInstance()的所有调用者之间共享。

但是这个实现有一个问题。多线程应用程序将在创建单个实例时具有竞争条件。如果多个执行线程同时(或大约)同时命中getInstance()方法,它们将各自看到INSTANCE成员为null。这将导致每个线程创建一个新的MySingleton实例,然后设置INSTANCE成员。

private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

这里我们使用方法签名中的synchronized关键字来同步getInstance()方法。这肯定会解决我们的竞争状况。线程现在将阻止并一次输入一个方法。但它也会产生性能问题。此实现不仅同步单个实例的创建,还将所有调用同步到getInstance(),包括读取。读取不需要同步,因为它们只返回INSTANCE的值。由于读取将构成我们调用的大部分(请记住,实例化仅在第一次调用时发生),我们将通过同步整个方法而产生不必要的性能损失。

private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

这里我们将同步从方法签名移动到包装MySingleton实例创建的同步块。但这能解决我们的问题吗?好吧,我们不再阻止阅读,但我们也向前退了一步。多个线程将同时或大约同时命中getInstance()方法,并且它们都将INSTANCE成员视为null。然后,他们将点击同步块,其中一个将获得锁并创建实例。当该线程退出该块时,其他线程将争用该锁,并且每个线程将逐个通过该块并创建该类的新实例。所以我们回到了我们开始的地方。

private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

这里我们从INSIDE块发出另一张支票。如果已经设置了INSTANCE成员,我们将跳过初始化。这称为双重检查锁定。

这解决了我们的多实例化问题。但是,我们的解决方案又一次提出了另一项挑战。其他线程可能不会“看到”INSTANCE成员已更新。这是因为Java优化了内存操作。线程将变量的原始值从主存储器复制到CPU的缓存中。然后,将对值的更改写入该缓存并从中读取。这是Java的一项功能,旨在优化性能。但这给我们的单例实现带来了问题。第二个线程 - 由不同的CPU或核心使用不同的缓存处理 - 将不会看到第一个线程所做的更改。这将导致第二个线程将INSTANCE成员视为null,从而强制创建我们的单例的新实例。

private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

我们通过在INSTANCE成员的声明中使用volatile关键字来解决这个问题。这将告诉编译器始终读取和写入主内存,而不是CPU缓存。

但这种简单的改变需要付出代价。因为我们绕过CPU缓存,所以每次操作易失性INSTANCE成员时我们都会受到性能影响 - 我们会这样做4次。我们仔细检查存在(1和2),设置值(3),然后返回值(4)。有人可能认为这条路径是边缘情况,因为我们只在第一次调用方法时创建实例。也许创作的表现受到了影响。但即使是我们的主要用例read也会对volatile组件进行两次操作。一旦检查存在,再次返回其值。

private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

由于性能命中是由于直接在volatile成员上操作,让我们将局部变量设置为volatile的值,然后对local变量进行操作。这将减少我们对易失性操作的次数,从而回收我们失去的一些性能。请注意,当我们进入synchronized块时,我们必须再次设置本地变量。这可确保它在我们等待锁定时发生的任何更改都是最新的。

我最近写了一篇关于此事的文章。 Deconstructing The Singleton。您可以找到有关这些示例的更多信息以及"持有者"的示例。那里的模式。还有一个真实的例子展示了双重检查的volatile方法。希望这会有所帮助。

答案 8 :(得分:0)

Singloton类是每次都获得相同对象的类。 当你想限制一个类创建多个对象时,我们需要Singleton类。

例如:

public class Booking {
    static Booking b = new Booking();
    private Booking() { }
    static Booking createObject() { return b; }
}

要创建此类的对象,我们可以使用:

Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();
Booking b1, b2, b3, b4;
b1 = Booking.createObject();
b2 = Booking.createObject();

b1b2指的是同一个对象。

答案 9 :(得分:0)

在创建单例类时,您应该考虑以下属性

  1. 反思
  2. 多线程
  3. 克隆
  4. 序列化

如果您的课程中没有Clone或Serialization接口,我认为以下课程最好作为单例课程。

public class JavaClass1 {
private static JavaClass1 instance = null;
private JavaClass1() {
    System.out.println("Creating -------");
    if (instance != null) { // For Reflection
        throw new RuntimeException("Cannot create, please use getInstance()");
    }
}

public static JavaClass1 getInstance() {
    if (instance == null) {
        createInstance();
    }
    return instance;
}
 private static synchronized void createInstance() { // for multithreading
    if (instance == null) {
        instance = new JavaClass1();
    }
}}