主目录:https://blog.csdn.net/qq_52681418/article/details/114828850
设计模式-单例模式
单例模式实际上就是类自己为自己创建对象,即内存里该类每次只会存在一个对象。
根据实例化的时机可分为:
- 懒汉式
- 饿汉式:代表有Runtime
此模式违背单一职责原则,因为它无法继承和实现,只关心自己内部
1.饿汉式
饿汉式实现简单,
- 通过私有静态成员变量实例化自身:创建自身对象
- 私有化构造器:禁止外部进行实例化
- 静态方法:外部获取实力的入口。
public class Single1 {
//处l直接赋值,还可以在静态代码块赋值
private static Single1 single1=new Single1();
//私有化构造器,使外部无法new对象
private Single1(){}
//外部获取对象的方法
public static Single1 getSingle1(){
return single1;
}
}
这种类型简单粗暴,但存在问题:不管用不用该对象都会被创建,容易造成资源浪费。
2.懒汉式
使用懒汉式,对象不会在类加载时创建,而是在外部调用时创建
public class Single2 {
private static Single2 single2;
private Single2(){}
public Single2 getSingle2(){
if(single2==null){ //如果对象不存在,则创建,否则直接返回已有对象
single2=new Single2();
}
return single2;
}
}
可以发现1、2两种方式直接只是成员变量赋值的时机不同。
严格意义上讲,2不是单例模式。在高并发场景下假设多个线程同时访问getSingle2(),在new执行之前,if条件一定成立,此时就可能会出现多个线程通过if判断的情况,故而会出现多个实例。
3.同步锁机制
在2的基础上加工synchroized同步锁,线程进入该方法后锁住,阻止其它线程进入。
public class Single2 {
private static Single2 single2;
private Single2(){}
public synchronized Single2 getSingle2(){
if(single2==null){ //如果对象不存在,则创建,否则直接返回已有对象
single2=new Single2();
}
return single2;
}
}
某个线程进入方法后,立刻锁定该方法,其他线程无法进入,直到锁被释放,这样有效防止if被突破。
但它仍有问题:在创建时很安全,但对象创建之后线程全部都是取操作,加锁后只能逐个去取,影响性能。
4.双重检查锁
第一次创建时,会加锁,对象创建完毕后,由于外层条件不成立,因此不会进入同步块。
public class Single2 {
private volatile static Single2 single2;
private Single2(){}
public Single2 getSingle2(){
if(single2==null) {
synchronized (Single2.class) {
if (single2 == null) {
single2 = new Single2();
}
}
}
return single2;
}
}
可以发现成员变量多出了 volatile 关键字,它的作用时不让JVM对指令优化和重排,防止多线程情况下出现空指针异常。
5.静态内部类
JVM在加载外部类时不会加载静态内部类。静态内部类实现简单,只是讲实例化操作转移到静态内部类中。
public class Single2 {
private Single2(){}
//静态内部类
private static class Inner{
private static final Single2 single2=new Single2();
}
public Single2 getSingle2(){
return Inner.single2;
}
}
6.枚举
enum Single3{
INSTANCE;
}
7.单例防止被破坏
怎么破坏?
- 序列化操作
- java反射操作
1.防止序列化破坏单例
在类中添加如下方法即可。
//此方法会在序列化时自动调用 public Object readResolve(){ return Single2.single2; }
2.防反射破坏单例
反射可以修改构造器的作用域,因此可以破坏单例,我们需要对构造方法进行改造。
//private Single2(){}
private static boolean is=false;
private Single2(){
synchronized (Single2.class){
if (is){//如果被访问过了,抛出一个异常
throw new IllegalArgumentException();
}
is=true;
}
}
实例化时会调用构造方法,在类中添加一个状态,记录构造器是否被使用过,如果使用过就抛异常。
3.改进后的双重检查锁
public class Single2 {
private volatile static Single2 single2;
//----------------------------------------------------用于构造
private static boolean is=false;
private Single2(){
synchronized (Single2.class){
if (is){//如果被访问过了,抛出一个异常
throw new IllegalArgumentException();
}
is=true;
}
}
//----------------------------------------------------用于获取实例
public Single2 getSingle2(){
if(single2==null) {
synchronized (Single2.class) {
if (single2 == null) {
single2 = new Single2();
}
}
}
return single2;
}
//----------------------------------------------------防序列化破坏
public Object readResolve(){
return Single2.single2;
}
}