白话设计模式-烂大街的单例模式
说到单例模式,我相信再看的都不会陌生,毕竟都烂大街了。
这篇文章,你将会看到:
1.什么是单例模式
2.单例模式要注意什么
3.单例模式的5种方式具体实现
一.什么是单例模式。
就好比一个人,每次叫他(调用)的时候,都是同一个对象,并不是一个新的对象。单例模式只会有一个实例,那就是你,你将会是给别人提供的唯一的一个实例。
一个实例&&唯一 = 单例模式
二.单例模式要注意什么
1.要保证一个实例,在多线程环境下,就必须要保证线程安全。
2.尽可能少的暂用资源,也就是懒加载lazy
三.单例模式的5种方式
这5种方式可以总结为:饿懒Double,在内部枚举。
1. 饿汉模式
饿汉就是类加载就创建实例,这个实例一直在。
含义:饿就饿在管你吃不吃,先new起来给你准备好。所以不用考虑线程
缺点:占用内存
适用:占用内存小的对象
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}
2.懒汉模式
懒汉就是用到的时候再去创建实例
含义:我就是懒,你叫我去做,我再去做,我才不会给你提前准备好呢。
缺点:需要考虑线程安全,下面的懒汉demo是不安全的
适用:使用少,占内存大的对象
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
3.Double check(面试常问)
升级的懒汉模式,解决了线程安全问题,但是又不像直接加synchronized那么笨重,重点在于一个double check
为什么需要double check?
第一个check:防止已创建的对象进入,挡掉了大部分进入。
第二个check:在两个线程都同时达到synchronized的时候,线程A执行完毕并new了对象,这个时候排到线程B进入,如果没有判断,则又会new对象。
为什么要加volatile?
防止指令重排。对于volatile通过MESI缓存一致性协议,通俗的讲就是在某个线程改变值的情况下,会去通知其他线程值改变了,再写进内存里,所以它能保证执行的顺序性。
volatile关键字详细讲解将会在未来的并发编程专栏里为大家呈现。
public class Singleton {
private static volatile Singleton instance = null;//volatile
private Singleton() {}
public static Singleton newInstance() {
if (null == instance) {// Single Checked
synchronized (Singleton.class) {
if (null == instance) {// Double checked
instance = new Singleton();
}
}
}
return instance;
}
}
4.内部类
内部类只会在用的时候才会去new,解决了饿汉的内存占用,又解决了懒汉的线程安全问题。
public class Singleton {
private static class SingletonHolder {
public static Singleton instance = new Singleton();
}
private Singleton() {}
public static Singleton newInstance() {
return SingletonHolder.instance;
}
}
5.枚举
通过Singleton.INSTANCE获得单例对象,但是它的缺点和饿汉一样,就是占用内存。
但是它比其他方式好的就是:有效避免的反序列化来创造新的对象。
public enum Singleton {
INSTANCE;
}
今天给大家讲了最简单的单例模式,以及5种创建方式,只要记住饿懒Double,在内部枚举,以及double check相关的两个问题,那么你在单例模式的面试都不会有什么问题了。
喜欢本文章的,可以点一下关注按钮,你们的支持就是我最大的动力。