创建型设计模式 - 单例模式的八种写法

一、单例模式

单例模式有以下8种写法:

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

单例模式的使用场景:

需要频繁创建和销毁的对象;创建时耗时过多或消耗资源过多,但又经常用到的对象(比如session工厂、数据源等)

(1)饿汉式 - 静态常量写法

代码实现:

/**
 * 设计模式之单例模式
 * 饿汉式(静态常量)
 */
public class SingletonTest01 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //定义为常量,保证实例对象不变
    private final static Singleton instance = new Singleton();


    //通过此方法获取实例
    public static Singleton getInstance() {
        return instance;
    }

}

分析:

优点:

  • 使用方式简单,在类加载的时候创建实例对象,避免了线程同步问题

缺点:

  • 在类加载的时候创建实例对象,但不确定何时使用、是否使用,可能造成内存浪费

(2)饿汉式 - 静态代码块写法

代码实现:

/**
 * 设计模式之单例模式
 * 饿汉式(静态代码块写法)
 */
public class SingletonTest02 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton{

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton(){

    }

    //定义为常量,保证实例对象不变
    private final static Singleton instance;

    static {
        instance = new Singleton();
    }

    //通过此方法获取实例
    public static Singleton getInstance(){
        return instance;
    }

}

分析:

和静态常量一致,只不过初始化的位置不同,一个在静态代码块,一个直接在常量声明处初始化

(3)懒汉式 - 线程不安全

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(线程不安全)
 */
public class SingletonTest03 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

分析:

优点:

  • 满足随用随拿的特点,解决了内存浪费的问题

缺点:

  • 线程不安全,当多个线程访问时,可能创建多个实例,因此实际开发中不可使用

(4)懒汉式 - 线程安全 - 同步方法写法

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(同步方法)
 */
public class SingletonTest04 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

}

分析:

虽然解决了线程不安全问题,但锁的范围太大,效率低,开发中尽量不要使用

(5)懒汉式 - 线程安全 - 同步代码块写法

代码实现:

/**
 * 设计模式之单例模式
 * 懒汉式(同步代码块写法)
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static Singleton instance;

    //通过此方法获取实例
    public static Singleton getInstance() {
        if (instance == null) {
            //同步代码
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }

}

分析:

这种方式将同步锁缩小了范围,本意是解决效率问题,但又造成了线程不安全,因此开发中不可使用

(6)懒汉式 - 双重检查(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 双重检查
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //声明实例对象
    private static volatile Singleton instance;

    //双重判断 + 同步锁
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

分析:

既提高了效率也解决了线程安全问题,推荐使用这种方法

(7)懒汉式 - 静态内部类(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 静态内部类
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

class Singleton {

    //私有构造方法,使其不可在外部通过构造器实例化
    private Singleton() {

    }

    //静态内部类
    private static class SingletonInstance{
        private final static Singleton INSTANCE = new Singleton();
    }

    //获取实例
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }

}

分析:

利用了类加载机制,保证初始化实例时只有一个线程。Singleton类被装载时并不会被实例化,当调用getInstance方法时才会装载SingletonInstance

(8)懒汉式 - 枚举法(推荐使用)

代码实现:

/**
 * 设计模式之单例模式
 * 枚举
 */
public class SingletonTest08 {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println("两次获取的实例一样吗:" + (instance1 == instance2)); //true
    }
}

enum Singleton{
    INSTANCE;
}

分析:

不仅能规避线程不安全,还能防止反序列化重新创建新的对象


p.s. 所有代码和笔记均可在 我的GitHub 中获取,如果对您有帮助的话,可以点个 star 支持一下 🙈

posted @ 2020-03-06 20:17  农夫三拳有点疼~  阅读(218)  评论(0编辑  收藏  举报