单例设计模式--java进阶day16

1.单例设计模式

确保某个类的对象在内存中只占有一份,节省资源空间

2.实现单例设计模式

分为两种设计,一种是饿汉式,简单粗暴;一种是懒汉式,面试会问

[1]饿汉式

先将类的构造方法私有化,让用户无法直接new对象,保证对象唯一

接着,我们在类里new一个对象,供用户使用

但是s是非静态成员,只能new对象才能获取,可构造方法已经被我们私有化,因此,我们加上static修饰,让用户通过类名直接获取

接着,我们调用两次s,很明显,两次调用的对象是同一个

还需要优化代码,使用public修饰,放大权限;使用final修饰,防止唯一的对象被用户恶意篡改

最简单的饿汉式写法就已经写好了

但是,这样写没有体现出面向对象思想,面向对象是通过调用方法去解决问题,所以,我们再写一个方法返回s,让用户掉用方法去获取对象更为合适

既然写了方法去获取对象,那么我们就需要私有化s,防止用户再类名调用s,既然私有化了s,也就不再需要final修饰了 如图

[2]懒汉式

懒汉式第一步也是先私有化构造方法

所谓懒汉式,就是很懒,先声明变量s,不进行初始化

当用户在调用方法获取对象时,此时再new对象,将对象返回给用户

但是这种写法,不能保证对象唯一

因为new了两次对象,调用一次getInstance就new了一个对象,调用两次自然两个对象

所以,我们要在方法里加入判断,当s为null时再new对象,如果s被创建了就不为null,也就不会再创建对象

上图只是懒汉式的入门写法,而且存在弊端,在多线程并发操作时,可能会创建出多个对象

原因分析

假设线程1拿到执行权,s此时为null,符合条件,线程1进入if内部

线程1刚要创建对象时,线程2抢到执行权,s还是null,线程2也进入if内部

两条线程都进入了if内部,后续不会再进行if判断,直接new了两个对象

所以,我们要使用线程同步,当线程在new对象时,其他线程不要打扰,这样就可以避免创建多个对象

右键运行,都是同一份地址

代码还需要优化,这样写还是存在弊端,效率太低了

如图,当线程1抢到执行权,上锁,此时线程2进入了阻塞状态

执行完毕后,线程2抢到执行权,上锁,线程1进入阻塞状态(括号里的阻塞代表阻塞的次数)

假设线程1再次抢到执行权,上锁,线程2就又进入阻塞状态,此时线程2已经阻塞了两次了

执行完毕,线程2抢到执行权,上锁,线程1也阻塞了两次

所以,代码这样写,每次执行任务时,就必然会有一方线程阻塞,效率非常低


但是,我们又无法避免阻塞的情况。既然无法避免,我们就要思考如何减少阻塞情况的发生

如图,我们在上锁前加上一个判断

假设线程1抢到执行权,s初始为null,进入if内部,此时,线程2抢到执行权,也进入if内部

线程1继续抢夺执行权,上锁,线程2第一次阻塞,满足if条件,创建对象

线程1执行完毕后,回到最外面,此时s已经创建好了,即便线程1抢到执行权也不满足第一个if的条件,不会再继续进入内部上锁

此时,线程2还在if内部,上锁,发现s不为null,释放锁,返回对象,执行结束

这时线程1、2都在最外面,发现s不为null,不满足第一个if条件,那就都不会进入if内部上锁,都直接返回对象了

这就是完整的懒汉式写法,也叫延迟加载模式

posted @ 2025-05-18 16:53  直実  阅读(10)  评论(0)    收藏  举报