单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)。就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
就是类在内存中只能存在一个实例对象
饿汉式
所谓饿汉式,就是直接创建出类的实例化,然后用private私有化,对外只用静态方法暴露。
静态变量
步骤
- 构造器私有化
- 类的内部创建对象
- 向外暴露一个静态的公共方法
优点 | 缺点 |
---|---|
写法简单,在类装载的时完成实例化,避免了线程同步问题 | 类装载时完成实例化,没有达到LazyLoading的效果,若该实例从未使用,则会造成内存浪费 |
class Singleton { //私有化构造器 private Singleton() { } //内部创建对象实例 private final static Singleton instance = new Singleton(); //对外公有的静态方法 public static Singleton getInstance() { return instance; } }
静态代码块
将类的实例化放到静态代码块中的写法,基本同上。
class Singleton { //静态代码块 //私有化构造器 private Singleton() {} //内部创建对象实例 private static Singleton instance; static { // 在静态代码块中,创建单例对象 instance = new Singleton(); } //对外公有的静态方法 public static Singleton getInstance() { return instance; } }
测试代码(后面都可使用这个测试代码,不在赘述运行截图,可以自己试试):
public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance1 == instance2); System.out.println("instance1.hashCode=" + instance1.hashCode()); System.out.println("instance2.hashCode=" + instance2.hashCode()); }
懒汉式
所谓懒汉式,就是在需要调用的时候再创建类的实例化。
线程不安全
起到了懒加载效果,但是只能在单线程使用,多线程会不安全,因为当多个线程并发同时判断instance为空时,就会相应的实例化多个对象。
class Singleton { //线程不安全 private static Singleton instance; private Singleton() {} public static Singleton getInstance() { //调用时才实例化对象,懒汉式 if(instance == null) { instance = new Singleton(); } return instance; } }
线程安全
上面线程不安全,那上锁不就好了,使用synchronized关键字。
这样虽然解决了线程安全,但其实实例化操作只做一次,而获取实例(即getInstance)的操作是很多次的,把调用的方法加上同步,会大大降低效率。
class Singleton { //线程安全 private static Singleton instance; private Singleton() {} //synchronized同步处理 public static synchronized Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } }
双重检查
上面代码效率低,那在同步前判断一下有没有实例化不就好了,若没有实例化则用同步方法new一个,否则直接return即可。即所谓的双重检查。
需要用到关键字volatile,防止指令重排。如果不用volatile关键字,就会和线程不安全情形一样,在if判断那会有并发。
class Singleton { //双重检查 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; //否则直接return } }
这样既实现了懒加载,又保证了线程安全。
静态内部类
静态内部类在外部类装载时不会实例化,当调用的时候才会装载并实例化,且JVM保证了其装载时的线程安全性。也能保证懒加载和线程安全,有点像自带版的双重检查。
class Singleton { private static volatile Singleton instance; private Singleton() {} //静态内部类,包含一个静态属性:Singleton private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } //对外公有的静态方法,直接返回SingletonInstance.INSTANCE public static synchronized Singleton getInstance() { return SingletonInstance.INSTANCE; } }
枚举
其实,使用枚举也能实现单例模式,不仅能避免多线程同步问题,也能防止反序列化重新创建新的对象。
enum Singleton { INSTANCE; //属性 public void say() { System.out.println("记得三连~"); } }
对应测试:
public static void main(String[] args) { Singleton instance1 = Singleton.INSTANCE; Singleton instance2 = Singleton.INSTANCE; System.out.println(instance1 == instance2); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); instance1.say(); }
总结
单例模式使用的场景:
需要频繁的进行创建和销毁的对象创建对象时耗时过多或耗费资源过多(重量级对象)经常用到的对象、工具类对象、频繁访问数据库或文件的对象(数据源、session工厂)
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注自学编程网的更多内容!
- 本文固定链接: https://www.zxbcw.cn/post/217741/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)