单例模式
本文章根据b站狂神说单例模式视频教程整理
b站狂神说单例模式视频链接
https://www.bilibili.com/video/BV1K54y197iS?from=search&seid=3102450277699477443
饿汉式单例
package com.kuang;
/**
* @Author: Abe
* Date: 2020/12/5 14:24
* 饿汉式单例模式
*/
public class Hungry {
//造成资源浪费
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
//私有化构造器
private Hungry() {
}
private final static Hungry HUNGRY = new Hungry();
//提供一个方法,供其他对象来调用
public static Hungry getInstance(){
return HUNGRY;
}
}
总结 :饿汉式单例能保证线程安全,并且也只会有一个实例,但是这种方式会在程序一启动的时候把对象创建好,这样就有可能造成资源浪费,影响性能。
懒汉式单例
package com.kuang;
/**
* @Author: Abe
* Date: 2020/12/5 14:35
*/
public class LazyMan {
//构造器私有化
public LazyMan() {
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
总结:以上这种方式,在多线程的情况下,就会出现问题,会出现多个实例,我们在实际开发中一般都是会出现多线程并发的情况。所以需要改进
改进代码一
//双重检测锁模式 dcl懒汉模式
public static LazyMan getInstance(){
if(lazyMan == null) {
synchronized (LazyMan.class){
if(lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
总结:这种情况下,看起来是没有问题的,并且测试的话,实际情况也是只有实例一个对象,这就符合单例模式,但是在极端情况下还是会有问题的。new LazyMan() 不是原子性操作,实际的操作如下
- 分配内存空间
- 执行构造方法,初始化对象
- 把这个对象指向这个空间
但是在实际中,有可能出现指令重排的现象,实际上是按照顺序执行的,但是也有可能出现执行 1>3>2
这样的话,在上面的代码中就会有问题,在判断对象是否为空的时候,出现不为空,但是还没有完成构造。
办法
private volatile static LazyMan lazyMan;
加上volatile就能解决
静态内部类实现
package com.kuang;
/**
* @Author: Abe
* Date: 2020/12/5 14:59
* 静态内部类
*/
public class Holder {
//构造器私有化
public Holder() {
}
public Holder getInstace() {
return InnerClass.HOLDER;
}
//静态内部类
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
枚举单例
package com.kuang;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @Author: Abe
* Date: 2020/12/5 15:22
* 枚举单例
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
static class Test{
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance == instance2);
}
}
}
使用枚举可以防止使用反射去破坏单例模式