单例模式

官方定义

所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)

举个最常见的例子,Sping中的bean默认都是单例模式,每个bean定义只生成一个对象实例,每次getBean请求获得的都是此实例。

单例模式的八中写法

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举方法

验证法:

package singletonpattern.hungrydemo;

public class SingletonDemo {
    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());
        System.out.println(instance1.hashCode());

    }
}

饿汉式(静态常量)

class Singleton {

    //一、构造器私有化,防止外部用构造器
    private Singleton() {
    }

    //二、类的内部创建对象 final static
    private static final Singleton singleton = new Singleton();

    //三、对外提供公共的静态方法,返回该类唯一的对象实例
    public static Singleton getInstance() {
        return singleton;
    }
}

写法分析

  • 优势:简单 避免多线程的同步问题
  • 劣势:没有实现懒加载的效果,内存的浪费

饿汉式(静态代码块)

class Singleton {

    //一、构造器私有化,防止外部用构造器
    private Singleton() {
    }

    //二、类的内部创建对象 final static
    private static final Singleton singleton;
    
    static {
         singleton = new Singleton();
    }

    //三、对外提供公共的静态方法,返回该类唯一的对象实例
    public static Singleton getInstance() {
        return singleton;
    }
}

写法分析

  • 优势:简单 避免多线程的同步问题
  • 劣势:没有实现懒加载的效果,内存的浪费

懒汉式(线程不安全)

class Singleton {
    // 构造器私有化
    private Singleton() {
    }

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共方法的静态方法的时候,来判断
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

写法分析

  • 优势:起到了懒加载的效果,不会造成内存浪费
  • 劣势:线程不安全

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

class Singleton {
    // 构造器私有化
    private Singleton() {
    }

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共方法的静态方法的时候,来判断
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式(线程安全,同步代码块)

class Singleton {
    // 构造器私有化
    private Singleton() {
    }

    //类的内部提供对象
    private static Singleton singleton;

    //对外提供公共方法的静态方法的时候,来判断
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

不推荐的,解决不了的线程安全问题

双重检查

class Singleton {
    private Singleton() {
    }
    private static Singleton singleton;
    //加入双重检查机制
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

写法分析
实际开发中推荐使用这种方法

  • 线程安全 解决了线程安全问题
  • 懒加载 不会造成内存的浪费
  • 效率很高

可能出现的问题
我们认为的new Singleton()操作

1)分配内存地址M
2)在内存M上初始化Singleton对象
3)将M的地址赋值给instance变量

JVM编译优化后(指令重排)可能new Singleton()操作

1)分配内存空间M
2)将M的地址赋值给instance变量
3)在内存M上初始化Singleton对象

image
会有可能发生空指针异常
解决的方式
只需要一个关键字 volatile 来禁止指令重排

class Singleton {
    private Singleton() {
    }
    private static volatile Singleton singleton;
    //加入双重检查机制
    public static  Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

扩展-Volatile
轻量级的同步机制(低配版)没有保证原子性
三大特性?
保证可见性
其中一个线程修改了主内存变量的值,要写回主内存,并要及时通知其他内存可见
没有保证原子性
(没法)不能保证不可分割,完整,要么同时成功,要么同时失败
禁止指令重排
和底层内存屏障相关,避免多线程下出现指令乱序的情况

静态内部类

class Singleton {
    private Singleton() {
    }

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

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

写法分析

  • 不会出现线程安全问题
  • JVM来帮我们保证线程的安全性
  • 利用静态内部类的特点

枚举方法

enum Singleton{
    INSTANCE;//属性
}

写法分析
不仅可以避免线程安全问题 推荐大家使用

注意事项

1.单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是new

单例模式的使用场景

  • 对于一些需要频繁创建销毁的对象
  • 重量级的对象
  • 经常使用
  • 工具类对象
  • 数据源
  • ...
posted @ 2021-04-17 12:04  我等着你  阅读(50)  评论(0编辑  收藏  举报