singleton设计模式_Java Singleton设计模式最佳实践与示例

singleton设计模式

Java Singleton Pattern is one of the Gangs of Four Design patterns and comes in the Creational Design Pattern category.

Java Singleton模式“四个设计”模式帮派之一 ,位于“ 创新设计模式”类别中。

From the definition, it seems to be a very simple design pattern but when it comes to implementation, it comes with a lot of implementation concerns.

从定义上看,这似乎是一个非常简单的设计模式,但是当涉及到实现时,它会带来很多实现方面的问题。

The implementation of Java Singleton pattern has always been a controversial topic among developers. Here we will learn about Singleton design pattern principles, different ways to implement the Singleton design pattern and some of the best practices for its usage.

Java Singleton模式的实现一直是开发人员中有争议的话题。 在这里,我们将学习Singleton设计模式的原理,实现Singleton设计模式的不同方法以及使用它的一些最佳实践。

单例模式 (Singleton Pattern)

  • Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine.

    单例模式限制了一个类的实例化,并确保在Java虚拟机中仅存在该类的一个实例。
  • The singleton class must provide a global access point to get the instance of the class.

    单例类必须提供全局访问点才能获取该类的实例。
  • Singleton pattern is used for logging, drivers objects, caching and thread pool.

    单例模式用于日志记录 ,驱动程序对象,缓存和线程池
  • Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.

    Singleton设计模式还用于其他设计模式,例如Abstract FactoryBuilderPrototypeFacade等。
  • Singleton design pattern is used in core java classes also, for example java.lang.Runtime, java.awt.Desktop.

    Singleton设计模式还用于核心Java类,例如java.lang.Runtimejava.awt.Desktop

Java单例模式实现 (Java Singleton Pattern Implementation)

To implement a Singleton pattern, we have different approaches but all of them have the following common concepts.

要实现Singleton模式,我们有不同的方法,但是所有方法都具有以下共同概念。

  • Private constructor to restrict instantiation of the class from other classes.

    私有构造函数,用于限制该类从其他类的实例化。
  • Private static variable of the same class that is the only instance of the class.

    同一类的私有静态变量,是该类的唯一实例。
  • Public static method that returns the instance of the class, this is the global access point for outer world to get the instance of the singleton class.

    返回类实例的公共静态方法,这是外部世界获取单例类实例的全局访问点。

In further sections, we will learn different approaches of Singleton pattern implementation and design concerns with the implementation.

在进一步的章节中,我们将学习Singleton模式实现的不同方法以及与实现有关的设计问题。

1.渴望初始化 (1. Eager initialization)

In eager initialization, the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it.

在急切的初始化中,在加载类时会创建Singleton类的实例,这是创建Singleton类的最简单方法,但是它存在一个缺点,即使客户端应用程序可能不使用它也会创建该实例。

Here is the implementation of the static initialization singleton class.

这是静态初始化单例类的实现。

package com.journaldev.singleton;

public class EagerInitializedSingleton {
    
    private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
    
    //private constructor to avoid client applications to use constructor
    private EagerInitializedSingleton(){}

    public static EagerInitializedSingleton getInstance(){
        return instance;
    }
}

If your singleton class is not using a lot of resources, this is the approach to use. But in most of the scenarios, Singleton classes are created for resources such as File System, Database connections, etc. We should avoid the instantiation until unless client calls the getInstance method. Also, this method doesn’t provide any options for exception handling.

如果您的单例类没有使用大量资源,则可以使用这种方法。 但是在大多数情况下,都是为文件系统,数据库连接等资源创建Singleton类的。除非客户端调用getInstance方法,否则应避免实例化。 另外,此方法不提供任何用于异常处理的选项。

2.静态块初始化 (2. Static block initialization)

Static block initialization implementation is similar to eager initialization, except that instance of class is created in the static block that provides option for exception handling.

静态块初始化实现与热切初始化类似,不同的是,类的实例是在提供了异常处理选项的静态块中创建的。

package com.journaldev.singleton;

public class StaticBlockSingleton {

    private static StaticBlockSingleton instance;
    
    private StaticBlockSingleton(){}
    
    //static block initialization for exception handling
    static{
        try{
            instance = new StaticBlockSingleton();
        }catch(Exception e){
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
    }
    
    public static StaticBlockSingleton getInstance(){
        return instance;
    }
}

Both eager initialization and static block initialization creates the instance even before it’s being used and that is not the best practice to use. So in further sections, we will learn how to create a Singleton class that supports lazy initialization.

急切的初始化和静态块初始化都在使用实例之前就创建了实例,这不是最佳实践。 因此,在其他部分中,我们将学习如何创建支持延迟初始化的Singleton类。

Read: Java static

阅读Java静态

3.延迟初始化 (3. Lazy Initialization)

Lazy initialization method to implement Singleton pattern creates the instance in the global access method. Here is the sample code for creating Singleton class with this approach.

实现单例模式的惰性初始化方法在全局访问方法中创建实例。 这是使用这种方法创建Singleton类的示例代码。

package com.journaldev.singleton;

public class LazyInitializedSingleton {

    private static LazyInitializedSingleton instance;
    
    private LazyInitializedSingleton(){}
    
    public static LazyInitializedSingleton getInstance(){
        if(instance == null){
            instance = new LazyInitializedSingleton();
        }
        return instance;
    }
}

The above implementation works fine in case of the single-threaded environment but when it comes to multithreaded systems, it can cause issues if multiple threads are inside the if condition at the same time. It will destroy the singleton pattern and both threads will get the different instances of the singleton class. In next section, we will see different ways to create a thread-safe singleton class.

上面的实现在单线程环境下可以很好地工作,但是对于多线程系统,如果多个线程同时位于if条件中,则可能导致问题。 它将破坏单例模式,并且两个线程都将获得单例类的不同实例。 在下一节中,我们将介绍创建线程安全的单例类的不同方法。

4.线程安全单例 (4. Thread Safe Singleton)

The easier way to create a thread-safe singleton class is to make the global access method synchronized, so that only one thread can execute this method at a time. General implementation of this approach is like the below class.

创建线程安全的单例类的更简单方法是使全局访问方法同步 ,以便一次仅一个线程可以执行此方法。 这种方法的一般实现类似于下面的类。

package com.journaldev.singleton;

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton(){}
    
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
    
}

Above implementation works fine and provides thread-safety but it reduces the performance because of the cost associated with the synchronized method, although we need it only for the first few threads who might create the separate instances (Read: Java Synchronization). To avoid this extra overhead every time, double checked locking principle is used. In this approach, the synchronized block is used inside the if condition with an additional check to ensure that only one instance of a singleton class is created.

上面的实现可以很好地工作并提供线程安全性,但是由于与同步方法相关的成本,它降低了性能,尽管我们仅对可能创建单独实例的前几个线程需要它(请参阅: Java Synchronization )。 为了避免每次这样的额外开销,使用了双重检查的锁定原理。 在这种方法中,在if条件中使用了同步块,并进行了附加检查,以确保仅创建一个singleton类的实例。

The following code snippet provides the double-checked locking implementation.

以下代码段提供了双重检查的锁定实现。

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

Read: Thread Safe Singleton Class

阅读线程安全单例类

5. Bill Pugh Singleton的实施 (5. Bill Pugh Singleton Implementation)

Prior to Java 5, java memory model had a lot of issues and the above approaches used to fail in certain scenarios where too many threads try to get the instance of the Singleton class simultaneously. So Bill Pugh came up with a different approach to create the Singleton class using an inner static helper class. The Bill Pugh Singleton implementation goes like this;

在Java 5之前,Java内存模型存在很多问题,并且上述方法在某些情况下会失败,在某些情况下,太多的线程试图同时获取Singleton类的实例。 因此,比尔·普格(Bill Pugh)提出了另一种方法,该方法使用内部静态帮助程序类创建Singleton 。 Bill Pugh Singleton的实现是这样的;

package com.journaldev.singleton;

public class BillPughSingleton {

    private BillPughSingleton(){}
    
    private static class SingletonHelper{
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }
    
    public static BillPughSingleton getInstance(){
        return SingletonHelper.INSTANCE;
    }
}

Notice the private inner static class that contains the instance of the singleton class. When the singleton class is loaded, SingletonHelper class is not loaded into memory and only when someone calls the getInstance method, this class gets loaded and creates the Singleton class instance.

注意私有内部静态类 ,其中包含单例类的实例。 加载SingletonHelper类时,不将SingletonHelper类加载到内存中,只有当有人调用getInstance方法时,该类才会加载并创建Singleton类实例。

This is the most widely used approach for Singleton class as it doesn’t require synchronization. I am using this approach in many of my projects and it’s easy to understand and implement also.

这是Singleton类使用最广泛的方法,因为它不需要同步。 我在许多项目中都使用了这种方法,而且也很容易理解和实现。

Read: Java Nested Classes

阅读Java嵌套类

6.使用反射破坏单例模式 (6. Using Reflection to destroy Singleton Pattern)

Reflection can be used to destroy all the above singleton implementation approaches. Let’s see this with an example class.

反射可用于销毁所有上述单例实现方法。 让我们用一个示例类来看看。

package com.journaldev.singleton;

import java.lang.reflect.Constructor;

public class ReflectionSingletonTest {

    public static void main(String[] args) {
        EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance();
        EagerInitializedSingleton instanceTwo = null;
        try {
            Constructor[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                //Below code will destroy the singleton pattern
                constructor.setAccessible(true);
                instanceTwo = (EagerInitializedSingleton) constructor.newInstance();
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(instanceOne.hashCode());
        System.out.println(instanceTwo.hashCode());
    }

}

When you run the above test class, you will notice that hashCode of both the instances is not same that destroys the singleton pattern. Reflection is very powerful and used in a lot of frameworks like Spring and Hibernate, do check out Java Reflection Tutorial.

当您运行上述测试类时,您会注意到两个实例的hashCode不相同,这会破坏单例模式。 反射非常强大,并在诸如Spring和Hibernate的许多框架中使用,请查阅Java Reflection Tutorial

7.枚举辛格顿 (7. Enum Singleton)

To overcome this situation with Reflection, Joshua Bloch suggests the use of Enum to implement Singleton design pattern as Java ensures that any enum value is instantiated only once in a Java program. Since Java Enum values are globally accessible, so is the singleton. The drawback is that the enum type is somewhat inflexible; for example, it does not allow lazy initialization.

为了通过反射来克服这种情况,Joshua Bloch建议使用Enum来实现Singleton设计模式,因为Java确保任何枚举值在Java程序中仅被实例化一次。 由于Java枚举值可全局访问,因此单例也是如此。 缺点是枚举类型有些不灵活。 例如,它不允许延迟初始化。

package com.journaldev.singleton;

public enum EnumSingleton {

    INSTANCE;
    
    public static void doSomething(){
        //do something
    }
}

Read: Java Enum

阅读Java枚举

8.序列化和单例 (8. Serialization and Singleton)

Sometimes in distributed systems, we need to implement Serializable interface in Singleton class so that we can store its state in the file system and retrieve it at a later point of time. Here is a small singleton class that implements Serializable interface also.

有时在分布式系统中,我们需要在Singleton类中实现Serializable接口,以便我们可以将其状态存储在文件系统中,并在以后的某个时间点检索它。 这是一个小的单例类,它也实现了Serializable接口。

package com.journaldev.singleton;

import java.io.Serializable;

public class SerializedSingleton implements Serializable{

    private static final long serialVersionUID = -7604766932017737115L;

    private SerializedSingleton(){}
    
    private static class SingletonHelper{
        private static final SerializedSingleton instance = new SerializedSingleton();
    }
    
    public static SerializedSingleton getInstance(){
        return SingletonHelper.instance;
    }
    
}

The problem with serialized singleton class is that whenever we deserialize it, it will create a new instance of the class. Let’s see it with a simple program.

序列化单例类的问题在于,每当我们反序列化它时,它将创建该类的新实例。 让我们用一个简单的程序看看它。

package com.journaldev.singleton;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

public class SingletonSerializedTest {

    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        SerializedSingleton instanceOne = SerializedSingleton.getInstance();
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
                "filename.ser"));
        out.writeObject(instanceOne);
        out.close();
        
        //deserailize from file to object
        ObjectInput in = new ObjectInputStream(new FileInputStream(
                "filename.ser"));
        SerializedSingleton instanceTwo = (SerializedSingleton) in.readObject();
        in.close();
        
        System.out.println("instanceOne hashCode="+instanceOne.hashCode());
        System.out.println("instanceTwo hashCode="+instanceTwo.hashCode());
        
    }

}

The output of the above program is;

上面程序的输出是:

instanceOne hashCode=2011117821
instanceTwo hashCode=109647522

So it destroys the singleton pattern, to overcome this scenario all we need to do it provide the implementation of readResolve() method.

因此,它破坏了单例模式,以克服这种情况,我们需要做的所有事情都提供了readResolve()方法的实现。

protected Object readResolve() {
    return getInstance();
}

After this, you will notice that hashCode of both the instances is same in the test program.

此后,您将注意到两个实例的hashCode在测试程序中相同。

Read: Java Serialization and Java Deserialization.

阅读Java序列化Java反序列化

I hope this article helps you in grasping fine details of Singleton design pattern, do let me know through your thoughts and comments.

希望本文能帮助您掌握Singleton设计模式的详细信息,并通过您的想法和意见让我知道。

翻译自: https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples

singleton设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值