23种设计模式之单例模式
单例模式
参考:
https://how2j.cn/k/class-object/class-object-singleton/349.html#nowhere
核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
优点
- 单例模式只生成一个实例,减少系统的性能开销
- 单例模式可以在系统设置全局访问点,优化共享资源访问
常见的五种单例模式实现
一、饿汉式
特点:线程安全,调用效率高,不能延时加载
缺点:无论这个类有没有被调用到,都会首先加载里面的资源,占用一定的内存空间,如果这个类长时间没有被用到,那么容易会造成空间浪费的结果。
/*
精髓所在
*/
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
private static GiantDragon instance = new GiantDragon();
//public static 方法,提供给调用者获取第7行定义的对象
public static GiantDragon getInstance(){
return instance;
}
}
/*
测试,后面相同
*/
public class TestGiantDragon {
public static void main(String[] args) {
//通过new实例化会报错
// GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
//都是同一个对象
System.out.println(g1==g2); // true
System.out.println(g1==g3); // true
}
}
二、懒汉式
懒汉式单例模式与饿汉式单例模式不同,只有在调用这个类的时候,才会创建实例
特点:线程安全,调用效率不高,可以延时加载
缺点:效率较低
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,用于指向一个实例化对象,但是暂时不创建内存空间,令其指向null
private static GiantDragon instance;
//public static 方法,返回实例对象 // 若存在并发,可添加synchronized修饰符
public static GiantDragon getInstance(){
//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
if(null==instance){
instance = new GiantDragon();
}
//返回 instance指向的对象
return instance;
}
}
三、DCL懒汉式(双重检测锁模式)
特点:不需要对整个方法进行同步,缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率
缺点:因为不是在类加载时就创建对象,因此存在线程安全问题,当第一个线程执行到创建对象的方法时,但还未出方法返回,此时第二个线程进入,发现instance不为空,但第一个线程此时还未出去,可能发送意想不到的安全问题
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//准备一个类属性,用于指向一个实例化对象,但是暂时不创建内存空间,令其指向null
//使用避免指令重排带来的线程安全问题
private static volatile GiantDragon instance;
//public static 方法,返回实例对象
//使用同步代码块提高效率
//现在不需要对整个方法进行同步,缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率
public static GiantDragon getInstance(){
//第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
if(null==instance){
synchronized (GiantDragon.class){
if(null==instance){
instance = new GiantDragon();
}
}
}
//返回 instance指向的对象
return instance;
}
}
四、静态内部类实现
使用静态内部类解决了线程安全问题,并实现了延时加载
缺点:反射机制有可能会破坏单例化
public class GiantDragon {
//私有化构造方法使得该类无法在外部通过new 进行实例化
private GiantDragon(){
}
//不会在外部类初始化时就直接加载,只有当调用了getInstance方法时才会静态加载,线程安全,final保证了在内存中只有一份
private static class InnerClass{
private static final SingletonDemo4 instance = new SingletonDemo4();
}
public static SingletonDemo4 getInstance() {
return InnerClass.instance;
}
}
五、枚举单例
最推荐使用的单例模式
特点:线程安全,调用效率高
缺点:不能延时加载(但是安全)
//枚举方式实现单例模式
public enum SingletonDemo5 {
INSTANCE;
public static SingletonDemo5 getInstance() {
return INSTANCE;
}
}
class SingletonDemo5Test {
public static void main(String[] args) {
SingletonDemo5 instance = SingletonDemo5.getInstance();
SingletonDemo5 instance1 = SingletonDemo5.getInstance();
System.out.println(instance == instance1); //true
}
}
默默努力也能成为一个不输别人的人