单例模式的八种类型

单例模式的八种类型

1. 单例设计模式介绍

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session 对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory就够,这是就会使用到单例模式。

2. 单例模式有八种方式:

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举

3.八种类型的代码实现及对比

3.1 饿汉式(静态常量)

优缺点:实现简单,但是没有达到lazy loading效果。可用但可能造成内存浪费。

package singleton;

/**
 * @Author Worm
 * @Date 2020/8/9 16:57
 * @Version 1.0
 **/
public class SingletonTest01 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());/*42121758*/
        System.out.println(instance1.hashCode());/*42121758*/
    }
}

//饿汉式(静态常量)
class Singleton {
    //    1.构造器私有化
    private Singleton() {

    }

    //2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();

    //提供一个公共静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}

3.2 饿汉式(静态代码块)

和上一种一样

package singleton;

/**
 * @Author Worm
 * @Date 2020/8/9 17:09
 * @Version 1.0
 **/
public class SingletonTest02 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());/*42121758*/
        System.out.println(instance1.hashCode());/*42121758*/
    }
}

//饿汉式(静态代码块)
class Singleton1 {
    //    1.构造器私有化
    private Singleton1() {

    }

    //2.本类内部创建对象实例
    private final static Singleton1 instance;/*final修饰的属性必须显式赋值*/

    static {
        instance = new Singleton1();
    }

    //提供一个公共静态方法,返回实例对象
    public static Singleton1 getInstance() {
        return instance;
    }
}

3.3 懒汉式(线程不安全)

虽然起到了lazy loading的效果。但是只能在单线程下使用,一但多个线程同时进入if条件,便可能产生多个实例。实际开发中不能使用这种方式。

package singleton.type2;


/**
 * @Author Worm
 * @Date 2020/8/9 17:16
 * @Version 1.0
 **/
public class SingletonTest03 {
    public static void main(String[] args) {
        System.out.println("懒汉式1,线程不安全~,实际开发不使用");
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

3.4 懒汉式(线程安全,同步方法)

虽然解决了线程不安全问题,但是效率太低。因为线程每次访问方法时都会同步,但实际只需要第一次保证同步就可以,之后的同步是毫无意义的。

package singleton.type3;


/**
 * @Author Worm
 * @Date 2020/8/9 17:16
 * @Version 1.0
 **/
public class SingletonTest04 {
    public static void main(String[] args) {
        System.out.println("懒汉式2,线程安全~,同步方法");
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    //加入同步处理的代码,解决线程安全问题。但每个线程每次都要同步,实际上这个方法只需执行一次实例化代码,后面都是直接return的。这样效率太低
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

3.5 错误的懒汉式改进

这种方法本想改进第四种,提高效率。但实际上本方法不能起到线程同步作用。

单例模式的八种类型_第1张图片

3.6 双重检查

Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
实际开发中推荐使用,既延迟加载,而且效率较高。

package singleton.type4;


/**
 * @Author Worm
 * @Date 2020/8/9 17:16
 * @Version 1.0
 **/
public class SingletonTest05 {
    public static void main(String[] args) {
        System.out.println("懒汉式,双重同步");
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }
}

class Singleton {
    //    volatile:使变量一有修改立马提交到主存里面去
    private static volatile Singleton instance;

    private Singleton() {

    }

    //加入双重检查代码,解决线程安全问题,同时解决懒加载问题
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }

            }

        }
        return instance;
    }
}

3.7 静态内部类

本方式使用类装载机制来保证初始化实例时只有一个线程。注意,内部类不会在主类装载时立即实例化,而是在调用getInstance方法,时才会装载SingletonInstance类,从而完成Singleton的实例化。 同样推荐使用。

package singleton.type5;


/**
 * @Author Worm
 * @Date 2020/8/9 17:16
 * @Version 1.0
 **/
public class SingletonTest06 {
    public static void main(String[] args) {
        System.out.println("懒汉式,静态内部类单例模式,推荐使用");
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance == instance1);//true
        System.out.println(instance.hashCode());
        System.out.println(instance1.hashCode());
    }
}

class Singleton {

    private static Singleton instance;

    private Singleton() {

    }

    //    静态内部类,该类中有一个静态属性Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    //直接返回SingletonInstance.INSTANCE
//    注意:上面的静态内部类不会随着主类加载就加载,而是会在下面的方法执行时才加载。
    public static Singleton getInstance() {

        return SingletonInstance.INSTANCE;
    }
}

3.8 枚举

这种方式是Effective Java作者Josh Bloch 提倡的方式。不仅能避免多线程同步问题,而 且还能防止反序列化重新创建新的对象。

package singleton.type6;

import java.sql.SQLOutput;

/**
 * @Author Worm
 * @Date 2020/8/9 20:09
 * @Version 1.0
 **/
public class SingletonTest08 {


    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        Singleton singleton1 = Singleton.INSTANCE;
        System.out.println(singleton == singleton1);/*true*/
        System.out.println(singleton.hashCode());
        System.out.println(singleton1.hashCode());
    }
}

//枚举,推荐使用
enum Singleton {
    INSTANCE;/*属性*/

    public void sayOK() {
        System.out.println("ok");
    }
}

4 jdk源码中的单例模式举例

单例模式的八种类型_第2张图片

5 单例模式的总结

单例模式保证系统内存中只存在一个对象,当想实例化一个单例类时,使用相应的获取对象方法而不是new。单例模式的使用场景:需要频繁的进行创建和销毁的对象,创建对象时消耗的资源过多(即重量级对象)但又需经常用到的对象,工具类对象,频繁访问数据库的对象(诸如数据源,session工厂等)。

你可能感兴趣的:(java设计模式,设计模式)