由浅入深之层层升级探索单例模式
时间:2022-07-24
本文章向大家介绍由浅入深之层层升级探索单例模式,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言:设计模式源于生活
单例的基本概念
单例模式确保某各类只有一个实例,而且自行实例化并向整个系统提供这个实例。选择单例模式就是为了避免不一致状态
单例模式特点
1、单例类只能有一个实例。 2、单例类必须自己创建自己唯一的实例。 3、单例类必须给所有其它对象提供这一实例。
单例模式的优缺点
1、单例类只有一个实例 2、共享资源,全局使用 3、节省创建时间,提高性能
单例模式应用场景
项目定义的配置文件都是单例 SpringIOC容器默认就是单例模式 线程池和数据库连接池都是单例
饿汉模式
public class SingletonV1 {
/**
* 当类初始化,就会创建该对象
*/
private static SingletonV1 singletonV1 = new SingletonV1();
/**
* 构造方法需要私有化,禁止初始化
*/
private SingletonV1() {
}
public static SingletonV1 getInstance() {
return singletonV1;
}
/**
* 测试单例
*
* @param args
*/
public static void main(String[] args) {
SingletonV1 instance1 = SingletonV1.getInstance();
SingletonV1 instance2 = SingletonV1.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
饿汉模式-静态代码块
public class SingletonV5 {
private static SingletonV5 singletonV5 = null;
//类加载读取
static {
singletonV5 = new SingletonV5();
}
public static SingletonV5 getInstance(){
return singletonV5;
}
public static void main(String[] args) {
SingletonV5 instance1 = SingletonV5.getInstance();
SingletonV5 instance2 = SingletonV5.getInstance();
System.out.println(instance1 == instance2);
}
}
懒汉模式-线程不安全
public class SingletonV2 {
private static SingletonV2 singleton001;
/**
* 构造方法需要私有化,禁止初始化
*/
private SingletonV2() {
}
/**
* 多线程下,可能会造成线程不安全,违背单例模式
* @return
*/
public static SingletonV2 getInstance() {
if (null == singleton001) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton001 = new SingletonV2();
}
return singleton001;
}
/**
* 测试单例
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
public void run() {
SingletonV2 instance = SingletonV2.getInstance();
System.out.println(instance);
}
}).start();
}
}
}
懒汉模式-线程安全
public class SingletonV3 {
private static SingletonV3 singletonV2;
private SingletonV3() {
}
/**
* 效率低,每次获取单例都需要上锁
* @return
*/
public static synchronized SingletonV3 getInstance() {
if (null == singletonV2) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singletonV2 = new SingletonV3();
}
return singletonV2;
}
/**
* 测试单例
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
public void run() {
SingletonV3 instance = SingletonV3.getInstance();
System.out.println(instance);
}
}).start();
}
}
}
双重检验锁
public class SingletonV4 {
private static SingletonV4 singletonV4;
private SingletonV4() {
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV4 getInstance() {
if (null == singletonV4) {
synchronized (SingletonV4.class) {
if (null == singletonV4) {
singletonV4 = new SingletonV4();
}
}
}
return singletonV4;
}
public static void main(String[] args) {
SingletonV4 instance1 = SingletonV4.getInstance();
SingletonV4 instance2 = SingletonV4.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
破解单例模式-基于双重检验锁
public class SingletonV7 {
private static SingletonV7 singletonV7;
private SingletonV7() throws Exception {
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV7 getInstance() throws Exception {
if (null == singletonV7) {
synchronized (SingletonV7.class) {
if (null == singletonV7) {
singletonV7 = new SingletonV7();
}
}
}
return singletonV7;
}
public static void main(String[] args) throws Exception {
//获取单例对象
SingletonV7 instance1 = SingletonV7.getInstance();
//通过反射创建对象,暴力破解单例模式
Class<?> aClass = Class.forName("com.dream.sunny.SingletonV7");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV7 instance2 = (SingletonV7) declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1 == instance2);
}
}
可以发现,我第一次获取完对象后,第二次通过反射的形式,来创建对象,所以变相的破解了单例模式
双重检验锁-三级校验
public class SingletonV8 {
private static SingletonV8 singletonV8;
private SingletonV8() throws Exception {
synchronized (SingletonV8.class){
if(singletonV8 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV8 getInstance() throws Exception {
if (null == singletonV8) {
synchronized (SingletonV8.class) {
if (null == singletonV8) {
singletonV8 = new SingletonV8();
}
}
}
return singletonV8;
}
public static void main(String[] args) throws Exception {
//获取单例对象
SingletonV8 instance1 = SingletonV8.getInstance();
// //通过反射创建对象,暴力破解单例模式
Class<?> aClass = Class.forName("com.dream.sunny.SingletonV8");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV8 instance2 = (SingletonV8) declaredConstructor.newInstance();
}
}
因为反射是需要通过无参构造方法进行创建对象,所以可以在无参构造方法,多加一层校验,判断单例对象是否存在
双重检验锁-三级校验-再次破坏
public class SingletonV9 {
private static SingletonV9 singletonV9;
private SingletonV9() throws Exception {
synchronized (SingletonV9.class){
if(singletonV9 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV9 getInstance() throws Exception {
if (null == singletonV9) {
synchronized (SingletonV9.class) {
if (null == singletonV9) {
singletonV9 = new SingletonV9();
}
}
}
return singletonV9;
}
public static void main(String[] args) throws Exception {
//通过反射创建对象,暴力破解单例模式
Class<?> aClass = Class.forName("com.dream.sunny.SingletonV9");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV9 instance2 = (SingletonV9) declaredConstructor.newInstance();
//获取单例对象
SingletonV9 instance1 = SingletonV9.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
双重检验锁-三级校验升级版
public class SingletonV10 {
private static SingletonV10 singletonV10;
private SingletonV10() throws Exception {
synchronized (SingletonV10.class){
if(singletonV10 != null){
throw new RuntimeException("不要试图使用反射破坏异常");
}else{
singletonV10 = this;
}
}
System.out.println("--初始化--");
}
/**
* 双重检验锁
* 能够保证线程安全,且效率高
* @return
*/
public static SingletonV10 getInstance() throws Exception {
if (null == singletonV10) {
synchronized (SingletonV10.class) {
if (null == singletonV10) {
singletonV10 = new SingletonV10();
}
}
}
return singletonV10;
}
public static void main(String[] args) throws Exception {
//通过反射创建对象,暴力破解单例模式
Class<?> aClass = Class.forName("com.dream.sunny.SingletonV10");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV10 instance2 = (SingletonV10) declaredConstructor.newInstance();
//获取单例对象
SingletonV10 instance1 = SingletonV10.getInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举类,实现单例模式(枚举实现单例模式是无法被破解的,因为枚举没有空的构造方法)
public enum SingletonV11 {
INSTANCE;
SingletonV11(){
System.out.println("初始化方法");
}
public static void main(String[] args) {
SingletonV11 instance1 = SingletonV11.INSTANCE;
SingletonV11 instance2 = SingletonV11.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
}
总结
到这里几种写法都介绍完了,至于选择用哪种形式的单例模式,取决于你的项目本身,是否是有复杂的并发环境,还是需要控制单例对象的资源消耗。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Git 常用命令
- Nodejs 一些细节 (持续更新)
- Jenkins 凭据使用
- React源码解读【一】API复习与基础
- choco 安装 和 mkcert 本地https
- js 函数柯里化(Currying)
- GPS数据Python解析及地图可视化
- 文稿:Ant Design从无到有,带你体悟大厂前端开发范式
- 在React中实现和Vue一样舒适的keep-alive
- uniapp获取接口数据,渲染在picker选择器里面
- 我们是这样一步一步实现分布式锁的
- 缓存并发神技,如何通过双 key 来解决缓存并发问题?
- LRU缓存淘汰算法实现方案,这次没人再说你不会开发
- JVM技术总结之三——类加载机制
- 为什么你每次被问到HashMap底层原理都一知半解,搞定它