Java 单例模式 Singleton

单例

单例的目的是确保一个类只有一个实例,并提供该实例的全局访问点。

  • [类]

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

懒汉式-线程不安全

懒汉式-单例,静态变量被延迟实例化,这样做的好处是节约资源,但是在多线程下,因为多线程同时进入if (uniqueInstance == null) ,并且此时 uniqueInstance 为 null,那么会有多个线程执行uniqueInstance = new Singleton(); 语句,这将导致实例化多次 uniqueInstance。

public class Singleton {
    private static Singleton unqueInstance = null;
    //私有构造函数
    private  Singleton(){

    }
    public static  Singleton getUnqueInstance(){
            if(unqueInstance==null){
                unqueInstance = new Singleton();
            }
            return unqueInstance;
    }
}

懒汉式-线程安全

线程不安全的原因是多个线程同时进入了共有静态函数中的if代码块里,所以只要在共有静态函数上加一个同步锁就可以防止这样的情况。

public class Singleton {
    private static Singleton unqueInstance = null;
    //私有构造函数
    private  Singleton(){

    }
    public static synchronized Singleton getUnqueInstance(){
            if(unqueInstance==null){
                unqueInstance = new Singleton();
            }
            return unqueInstance;
    }
}

但是其实这样笼统的修改会影响效率的问题,比如当unqueInstance不等于null的时候,应该直接返回,但是此时unqueInstance==null和unqueInstance!=null同时访问该方法,这会让线程阻塞时间过长,因此该方法有性能问题。

饿汉式-线程安全

public class Singleton {
    private static Singleton unqueInstance = new Singleton();
    //私有构造函数
    private  Singleton(){

    }
    public static  Singleton getUnqueInstance(){
            return unqueInstance;
    }
}

直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。

双重校验锁-线程安全

这个是对懒汉式线程安全的进一步改进,就像我们在之前的描述一样,线程不安全的原因是多个线程同时进入了共有静态函数中的if代码块里,我们只需要对if代码块加入同步锁,但是如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句。

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

静态内部类实现

当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

public class Singleton {

    private  Singleton(){
    }

    private  static  class SingletonHolder{
        private  static  final  Singleton INSTANCE = new Singleton();
    }

    public static  Singleton getUniqueInstance(){
        return  SingletonHolder.INSTANCE;
    }
}

枚举实现

public enum Singleton {

    INSTANCE;

    private String objName;


    public String getObjName() {
        return objName;
    }


    public void setObjName(String objName) {
        this.objName = objName;
    }


    public static void main(String[] args) {

        // 单例测试
        Singleton firstSingleton = Singleton.INSTANCE;
        firstSingleton.setObjName("firstName");
        System.out.println(firstSingleton.getObjName());
        Singleton secondSingleton = Singleton.INSTANCE;
        secondSingleton.setObjName("secondName");
        System.out.println(firstSingleton.getObjName());
        System.out.println(secondSingleton.getObjName());

        // 反射获取实例测试
        try {
            Singleton[] enumConstants = Singleton.class.getEnumConstants();
            for (Singleton enumConstant : enumConstants) {
                System.out.println(enumConstant.getObjName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(Java 单例模式 Singleton)