23种设计模式之单例模式

Vidonia ·
更新时间:2024-09-21
· 823 次阅读

单例模式 饿汉式: /** * 饿汉式单例(提前把对象创建) * 可能会浪费空间,提前把对象创建好了,但是不一定会用。 */ public class Hungry { private Hungry(){ } private final static Hungry HUNGRY=new Hungry(); public static Hungry getInstance(){ return HUNGRY; } } 懒汉式

单线程下可用,多线程下不可用,存在多线程并发问题。

/** * 懒汉式单例(当使用时再创建对象) * 单线程下可用 */ public class LazyMan { private LazyMan(){ } private static LazyMan lazyMan; public LazyMan getInstnce(){ if(lazyMan==null){ lazyMan=new LazyMan(); } return lazyMan; } }

用多线程并发测试

public class LazyMan { private 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{ lazyMan.getInstance(); }).start(); } } }

于是进阶为(加锁)DCL懒汉式(成为双重检测锁模式):

public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+"ok"); } private static LazyMan lazyMan; //双重检测锁模式的懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan(); } } } return lazyMan; } //多线程并发测试 public static void main(String[] args){ for(int i=0;ilazyMan.getInstance() ).start(); } } }

每一个线程开启,都只有一个单例对象。

DCL懒汉式单例再改进(添加volatle消除指令重排):

public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+"ok"); } private volatile static LazyMan lazyMan; //双重检测锁模式的懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan();//不是原子性操作 /**不是原子性操作会发生以下三操作 * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个空间 * * * 会导致123或132顺序执行 * 若A线程顺序是132,又来了一个B线程, * 则此时lazyMan没有完成构造,则会发生问题。(指令重排) */ } } } return lazyMan; } //多线程并发测试 public static void main(String[] args){ for(int i=0;ilazyMan.getInstance() ).start(); } } } 静态内部类 /** * 静态内部类 */ public class Holder { private Holder(){ } public static Holder getInstance(){ return InnerClass.HOLDER; } public static class InnerClass{ private static final Holder HOLDER=new Holder(); } }

以上单例方法都有问题,因为存在反射技术!!!

演示反射破解(两重检测锁模式)DCL进阶版懒汉式:

//反射 public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); }

在这里插入图片描述

进阶:在无参构造器中进行判断(成为三层检测锁模式),再执行:

public class LazyMan { private LazyMan(){ synchronized (LazyMan.class){ if(lazyMan!=null){ throw new RuntimeException("不要试图使用反射破坏异常"); } } } private volatile static LazyMan lazyMan; //双重检测锁模式的懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan();//不是原子性操作 /**不是原子性操作会发生以下三操作 * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个空间 * * * 会导致123或132顺序执行 * 若A线程顺序是132,又来了一个B线程, * 则此时lazyMan没有完成构造,则会发生问题。 */ } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }

在这里插入图片描述

使用反射再次破解时,把两个对象都用反射的方法newInstance() 创建出来:

//反射 public static void main(String[] args) throws Exception { // LazyMan instance1 = LazyMan.getInstance(); Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance1 = declaredConstructor.newInstance(); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); }

在这里插入图片描述
进阶:采用红路灯(添加muzi属性),再执行:

public class LazyMan { private static boolean muzi=false; private LazyMan(){ synchronized (LazyMan.class){ if(muzi==false){ muzi=true; } else{ throw new RuntimeException("不要试图使用反射破坏异常"); } } } private volatile static LazyMan lazyMan; //双重检测锁模式的懒汉式单例,DCL懒汉式 public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan=new LazyMan();//不是原子性操作 /**不是原子性操作会发生以下三操作 * 1.分配内存空间 * 2.执行构造方法,初始化对象 * 3.把这个对象指向这个空间 * * * 会导致123或132顺序执行 * 若A线程顺序是132,又来了一个B线程, * 则此时lazyMan没有完成构造,则会发生问题。 */ } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { // LazyMan instance1 = LazyMan.getInstance(); Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance1 = declaredConstructor.newInstance(); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }

在这里插入图片描述

反射破解(通过反射获取muzi属性,并修改):

//反射 public static void main(String[] args) throws Exception { // LazyMan instance1 = LazyMan.getInstance(); Field muzi = LazyMan.class.getDeclaredField("muzi"); muzi.setAccessible(true); Constructor declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan instance1 = declaredConstructor.newInstance(); muzi.set(instance1,false); LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); }

在这里插入图片描述

枚举 /** * 枚举 */ public enum EnumSingle { INSTANCE; public EnumSingle getInstance(){ return INSTANCE; } }
作者:纪伯伦的小弟



23种设计模式 设计模式 单例模式

需要 登录 后方可回复, 如果你还没有账号请 注册新账号