java中的Singleton类

Singleton class in java

本问题已经有最佳答案,请猛点这里访问。

只是我在想单件课的其他写作方式。那么这个类被认为是单例类吗?

1
2
3
4
5
6
7
8
9
10
11
12
      public class MyClass{
            static Myclass myclass;

            static { myclass = new MyClass();}

            private MyClass(){}

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

因为静态块只运行一次。


不,不是。你没有申报myClassprivate static finalgetInstance()也没有申报static。代码也没有真正编译。

以下是单例成语:

1
2
3
4
5
6
7
8
9
public class MyClass {
    private static final MyClass myClass = new MyClass();

    private MyClass() {}

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

应该是private,这样其他人就不能直接访问它了。应该是static,这样就只有一个了。它应该是final,这样它就不能被重新分配了。您还需要在声明过程中直接实例化它,这样您就不需要担心(那么多)线程。

如果加载很昂贵,因此您更喜欢单例加载的延迟加载,那么考虑单例持有者习惯用法,它根据需要进行初始化,而不是在类加载期间进行初始化:

1
2
3
4
5
6
7
8
9
10
11
public class MyClass {
    private MyClass() {}

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

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

不过,你应该在是否需要单件衬衫上打上大问号。通常不需要。静态变量、枚举、工厂类和/或依赖注入通常是更好的选择。


还有一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
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的有效Java,这是在Java中实现Simelton的最好方法。与涉及私有静态实例字段的实现不同,该实例字段可以通过滥用反射和/或序列化进行多次实例化,枚举保证为单例。

枚举单例的主要限制是它们总是在类加载时被实例化,并且不能被延迟地实例化。因此,例如,如果您想使用运行时参数实例化一个单例,那么您必须使用不同的实现(最好使用双重检查锁定)。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
    }
}


1
2
3
4
5
6
7
8
9
10
11
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();
    }
}


在Java中创建单体有3种方法。

  • 热切初始化单例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Test {
        private static final Test test = new Test();

        private Test() {
        }

        public static Test getTest() {
            return test;
        }
    }
  • 惰性初始化单例(线程安全)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
        }
    }
  • Bill Pugh单开门,带支架图案(最好是最好的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Test {
        private Test(){}

        private static class TestHolder {
            private static final Test test = new Test();
        }

        public static Test getInstance() {
            return TestHolder.test;
        }
    }

  • 您的类(原始代码,编辑前):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyClass {
        Myclass myclass;

        static { myclass = new MyClass();}

        private MyClass(){}

        public MyClass getInstance()
        {
            return myclass;
        }
    }

    不是真正的单身汉:

  • 字段myClass不是私有的,可以从外部读取和更改(假设您有一个实例来执行此操作)
  • 字段myClass不是静态的,不能在静态构造函数中访问(编译错误)
  • getInstance()方法不是静态的,因此需要一个实例来调用它
  • < BR>实际代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyClass {
        static Myclass myclass;

        static { myclass = new MyClass();}

        private MyClass(){}

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

    仍然有myClass不是私有的(也不是最终的)。声明它为final将有助于防止它从类内部被无意地更改。

    1
    private static final Myclass myclass;


    使用您的示例并使用GOF的实现方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;
        }
    }

    希望这有助于:


    创建singleton类时,应该考虑以下属性

  • 反射
  • 多线程
  • 克隆
  • 串行化
  • 如果类中没有克隆或序列化接口,我认为下面的类最好是单例类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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();
        }
    }}

    singloton类是每次获取相同对象的类。当您想限制一个类创建多个对象时,我们需要单例类。

    例如:

    1
    2
    3
    4
    5
    public class Booking {
        static Booking b = new Booking();
        private Booking() { }
        static Booking createObject() { return b; }
    }

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

    1
    2
    3
    4
    5
    6
    Booking b1, b2, b3, b4;
    b1 = Booking.createObject();
    b2 = Booking.createObject();
    Booking b1, b2, b3, b4;
    b1 = Booking.createObject();
    b2 = Booking.createObject();

    b1b2指的是同一对象。


    在这一点上,可能有点晚了,但是一个基本的实现看起来是这样的:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class MySingleton {

         private static MySingleton INSTANCE;

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

            return INSTANCE;
        }
        ...
    }

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

    但是这个实现有一个问题。多线程应用程序在创建单个实例时将具有竞争条件。如果多个执行线程同时(或前后)命中getInstance()方法,它们将每个线程都将实例成员视为空。这将导致每个线程创建一个新的mysingleton实例,然后设置实例成员。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private static MySingleton INSTANCE;

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

        return INSTANCE;
    }

    这里我们使用方法签名中的synchronized关键字来同步getInstance()方法。这肯定会改善我们的比赛条件。线程现在将一次阻塞并输入一个方法。但它也会造成性能问题。此实现不仅同步单个实例的创建,还同步对getInstance()的所有调用,包括读取。读取不需要同步,因为它们只返回实例的值。因为reads将占我们调用的大部分(记住,实例化只在第一个调用上发生),所以通过同步整个方法,我们将导致不必要的性能损失。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private static MySingleton INSTANCE;

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

        return INSTANCE;
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private static MySingleton INSTANCE;

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

        return INSTANCE;
    }

    在这里,我们从街区内再开出一张支票。如果已经设置了实例成员,我们将跳过初始化。这叫做双重检查锁定。好的。

    这就解决了多重实例化的问题。但我们的解决方案再一次提出了另一个挑战。其他线程可能看不到实例成员已被更新。这是因为Java如何优化内存操作。线程将变量的原始值从主内存复制到CPU的缓存中。然后,对值的更改将写入和读取该缓存。这是为优化性能而设计的Java特性。但这给我们的单例实现带来了一个问题。第二条线?-?由不同的CPU或核心处理,使用不同的缓存?-?将看不到第一个所做的更改。这将导致第二个线程将实例成员视为空,从而强制创建singleton的新实例。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private static volatile MySingleton INSTANCE;

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

        return INSTANCE;
    }

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

    但这种简单的改变是有代价的。因为我们绕过了CPU缓存,所以每次在易失性实例成员上操作时都会受到性能影响?-?我们做了4次。我们再次检查是否存在(1和2),设置值(3),然后返回值(4)。有人可能会说,这条路径是边缘情况,因为我们只在方法的第一次调用期间创建实例。也许对创作的影响是可以容忍的。但即使我们的主要用例reads也会对易失性成员进行两次操作。一次检查是否存在,再次返回其值。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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;
    }

    由于性能下降是由于直接在易失性成员上操作造成的,所以让我们将局部变量设置为易失性的值,并改为在局部变量上操作。这将减少我们对易失性操作的次数,从而恢复一些丢失的性能。注意,当我们进入同步块时,必须再次设置本地变量。这样可以确保它是最新的,并且在等待锁时发生了任何更改。好的。

    我最近写了一篇关于这个的文章。解构单重子。您可以在这里找到关于这些示例和"holder"模式的示例的更多信息。还有一个真实的例子展示了双重检查的易失性方法。希望这有帮助。好的。好啊。