时长:1h33min
课程目标:
》单例模式的应用场景
》IDEA下的多线程调试方式
》保证线程安全的单例模式策略
》掌握反射暴力攻击单例解决方案及原理分析
》序列化破坏单例的原理及解决方案
》掌握常见的单例模式写法
》原型模式的应用场景及常用写法
学习目的:
》深入掌握单例模式
》解决面试题
单例模式,Singleton Pattern.
定义:
是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏所有的构造方法【私有】
创建创建型模式。
确保在任何情况下都只有一个实例。
生活中单例体现:
国家主席,公司ceo,部门经理。
代码中单例类:
ServletContext,ServletConfig,ApplicationContext,DBPool
为什么它是单例的呢?
如:配置文件,只能有一个,否则该使用谁呢。【唯一性主权】
1.饿汉式单例
2.懒汉式单例
3.注册式单例
4.ThreadLocal单例
人很饿------》着急吃饭---》在类首次加载时创建实例【用不用,先吃饱再说】
package com.wf.singleton; /** * @ClassName HungrySingleton * @Description 饿汉式单例 * @Author wf * @Date 2020/4/29 17:28 * @Version 1.0 */ public class HungrySingleton { //首次加载静态成员 public static final HungrySingleton singleton = new HungrySingleton(); //构造私有 private HungrySingleton(){} //全局访问点 public static HungrySingleton getInstance(){ return singleton; } }
第二种写法:
package com.wf.singleton; /** * @ClassName HungryStaticSingleton * @Description 饿汉式单例---静态块初始化 * @Author wf * @Date 2020/4/29 17:31 * @Version 1.0 */ public class HungryStaticSingleton { //final保证反射不会改变实例 private static final HungryStaticSingleton instance; private HungryStaticSingleton(){} static{ instance = new HungryStaticSingleton(); } public static HungryStaticSingleton getInstance(){ return instance; } }
优点:
执行效率高,性能高【无锁】
缺点:
可能生成内存浪费,因为实例不被使用,也进行new .
说明:
这种写法,只能在小范围内使用。
人很懒------不着急-----等到使用时再创建
package com.wf.singleton; /** * @ClassName LazySimpleSingleton * @Description 懒汉式单例 * @Author wf * @Date 2020/4/29 17:43 * @Version 1.0 */ public class LazySimpleSingleton { private static LazySimpleSingleton instance; private LazySimpleSingleton(){} public static LazySimpleSingleton getInstance(){ if(null == instance) { instance = new LazySimpleSingleton(); } return instance; } }
优点:
节省内存空间,使用时再创建实例。
缺点:
线程不安全。性能差。
为什么会线程不安全呢?
线程不安全来自于getInstance方法,方法的调用者最终归结到线程。
当出现多个线程,同时调用getInstance方法时,线程间会抢占资源。如下所示:
这是理论上的分析。下面在idea中进行代码调试,说明这种线程不安全性。
【1】创建测试类
package com.wf.singleton; import org.junit.Test; public class LazySimpleSingletonTest { @Test public void getInstance() { Thread t1 = new Thread(new ExecutorThread()); Thread t2 = new Thread(new ExecutorThread()); t1.start(); t2.start(); System.out.println("end"); } }
【2】测试任务类
package com.wf.singleton; /** * @ClassName ExecutorThread * @Description 创建一个测试task * @Author wf * @Date 2020/4/29 18:53 * @Version 1.0 */ public class ExecutorThread implements Runnable { @Override public void run() { LazySimpleSingleton instance = LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() +":"+instance); } }
在任务类中,run方法,访问单例类,获取实例。并打印实例hash地址。当两个线程,打印hash地址相同时,
说明只有一个实例,是单例的。
不存在线程安全问题,否则,是线程非安全的。
【1】直接运行测试类,查看结果:
明显,可以看到,hash地址不同,创建现两个实例。
多次运行,可以发现,有时只会创建一个实例。这时,线程执行的随机性决定的。
我们可以得出结论,getInstance方法是线程非安全的。
从上面的运行,有两种结果:
》同一实例
a,两个线程顺序执行
b.两个线程都进入getInstance方法,if分支【后者覆盖前者】
第一个线程,创建出一个实例instance1
第二个线程,稍后第二个线程,也进入if,并执行new实例instance2,instance2就把instance1给覆盖掉了
最后,得到的实例都是instance2
》不同实例
同时进入if条件,按顺序返回
第一个线程new后,快速执行结束方法,打印实例1
第二个线程,再次new,打印实例2
断点设置如下:
注意:
3个断点,都设置成Thread多线程运行模式。设置方法:右击断点,弹出设置框,切换到Thread模式,然后Done.
然后,debug运行测试类:
在Frames窗口下,可以查看到所有的线程。现在,几个线程都是running状态,表示线程都抢到cpu资源,但是什么时候,
执行任务,由cpu来决定。
切换到Thread-0,然后执行下一步,先让它按正常状态执行。
Thread-0线程,先顺序执行完成,并打印.
然后,切换到第二个线程Thread-1,让他进入getInstance方法,经过if判断,由于instance已经创建,非null,条件不成立。
所以,两个线程创建一个实例。如下所示:
可以看到,最后打印相同的结果。
第二种情况 :调试后者覆盖前者
先让两个线程都进入if分支,如下所示:
Thread-0线程进入:
Thread-1线程进入:
然后,再切换到Thread-0,先执行new语句,如下所示:
再切换到,线程1,执行new操作,如下所示:
再切换到,Thread-0执行完成,打印结果。
再让Thread-1执行完成,打印结果:
第三种情况:不同实例
首先,让两个线程都进入getInstance方法的if分支。
然后,先让Thread-0线程执行完,并打印。
最后,Thread-1线程,再执行完成,并打印,如下所示:
出现不同的实例。
解决方案:
使用sycronized关键字,加同步锁,如下所示:
package com.wf.singleton; /** * @ClassName LazySimpleSingleton * @Description 懒汉式单例 * @Author wf * @Date 2020/4/29 17:43 * @Version 1.0 */ public class LazySimpleSingleton { private static LazySimpleSingleton instance; private LazySimpleSingleton(){} public synchronized static LazySimpleSingleton getInstance(){//使用synchronized解决线程安全性问题 if(null == instance) { instance = new LazySimpleSingleton(); } return instance; } }
问题解决了。
3.注册式单例
4.ThreadLocal单例
两种快捷键:
1.在要生成测试类的类里面,按ctrl+shift+t
–> create new test
原文:https://www.cnblogs.com/wfdespace/p/12804435.html