单例模式没你想的那么简单
网上到处都是懒汉,饿汉模式。给两个Demo就算过去了吧。
饿汉单例模式:在类加载的时候,就开始实例化了。
public class HungrySingleton { private static HungrySingleton one=new HungrySingleton(); private HungrySingleton(){} public static HungrySingleton getInstance(){ return one; } public static void main(String[] args) { HungrySingleton one1=HungrySingleton.getInstance(); HungrySingleton one2=HungrySingleton.getInstance(); System.out.println(one1==one2); } }
懒汉模式:在第一次获取实例化对象的时候,开始实例化。
public class LazySingleton { private static LazySingleton one=null; private LazySingleton() { } public static LazySingleton getInstance(){ if(one==null){ one=new LazySingleton(); } return one; } public static void main(String[] args) { LazySingleton one1=LazySingleton.getInstance(); LazySingleton one2=LazySingleton.getInstance(); System.out.println(one1 == one2); } }
无论何种模式先要把构造函数给私有化,否则就变成了“勤快汉”模式了;这名字是我瞎编的。
饿汉模式典型用法:Spring中IOC容器ApplictionContext,连接池.
懒汉模式典型用法:不知道啊。
饿汉模式没缺点,最多也就在没使用的时候,分配个内存空间。
下面着重说说懒汉模式,所以来个分割线吧。
=================================
懒汉单例模式的线程安全
上面的懒汉模式有线程安全问题,就是多个线程在同时执行的时候,怎么保证LazySingleton只被实例化了一次。
线程类:
public class ExectorThread implements Runnable { @Override public void run() { LazySimpleSingleton one=LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + ":" + one); } }
单例类:
public class LazySimpleSingleton { private static LazySimpleSingleton one=null; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance(){ if(one==null){ one= new LazySimpleSingleton(); } return one; } }
测试类:
public class LazyTest { public static void main(String[] args) { Thread t1=new Thread(new ExectorThread()); Thread t2=new Thread(new ExectorThread()); t1.start(); t2.start(); System.out.println("end"); } }
第一个线程吧one实例化完成之后,还没有来得及刷新到内存,第二个线程就把one读入内存,又进行了一次实例化。
最简单的办法就是给实例化方法getInstance()添加一个synchronized.
修改后代码如下
public class LazySimpleSingleton { private static LazySimpleSingleton one=null; private LazySimpleSingleton() { } public static synchronized LazySimpleSingleton getInstance(){ if(one==null){ one= new LazySimpleSingleton(); } return one; } }
这种模式有一个性能问题;比如100个线程在同时调用getInstance()的时候,99个全部都阻塞在这个位置了,
包括one已经不是空值的时候,依然在阻塞中;改造上面的代码,让已经实例化之后的线程不在阻塞。
1 public class LazySimpleSingleton { 2 3 private static LazySimpleSingleton one=null; 4 5 private LazySimpleSingleton() { 6 } 7 public static LazySimpleSingleton getInstance(){ 8 //索前判断是否实例化了,实例化了就不用进入synchronized中了 9 if(one==null){ 10 synchronized(LazySimpleSingleton.class){ 11 //上面one==null了,不代表此时还是null 12 if(one==null){ 13 one= new LazySimpleSingleton(); 14 } 15 return one; 16 } 17 } 18 return one; 19 } 20 }
反射破坏单例
以饿汉单例的Demo为例子进行改造。
1 public class HungrySingleton { 2 3 private static HungrySingleton one=new HungrySingleton(); 4 5 private HungrySingleton(){} 6 7 public static HungrySingleton getInstance(){ 8 return one; 9 } 10 11 public static void main(String[] args) throws Exception{ 12 HungrySingleton one1=HungrySingleton.getInstance(); 13 Constructor constructor=HungrySingleton.class.getDeclaredConstructor(null); 14 //强制访问构造器,包括私有成员 15 constructor.setAccessible(true); 16 HungrySingleton one2=(HungrySingleton) constructor.newInstance(); 17 System.out.println(one1==one2); 18 } 19 }
打印结果显示false.说明被实例化了两次;修改代码如下。
public class HungrySingleton { private static HungrySingleton one=new HungrySingleton(); private HungrySingleton(){ if(one!=null){ throw new RuntimeException("已经实例化过了,本次实例化失败"); } } public static HungrySingleton getInstance(){ return one; } public static void main(String[] args) throws Exception{ HungrySingleton one1=HungrySingleton.getInstance(); Constructor constructor=HungrySingleton.class.getDeclaredConstructor(null); //强制访问构造器,包括私有成员 constructor.setAccessible(true); HungrySingleton one2=(HungrySingleton) constructor.newInstance(); System.out.println(one1==one2); } }
打印结果:
序列化破坏单例模式
以饿汉模式为例:
public class SeriableSingleton implements Serializable { public final static SeriableSingleton one=new SeriableSingleton(); private SeriableSingleton() { } public static SeriableSingleton getInstance(){ return one; } }
测试类:
1 public class SeriableSingletonTest { 2 public static void main(String[] args) { 3 SeriableSingleton s1=null; 4 SeriableSingleton s2=SeriableSingleton.getInstance(); 5 6 FileOutputStream fos=null; 7 try { 8 fos=new FileOutputStream("one.obj"); 9 ObjectOutputStream oos=new ObjectOutputStream(fos); 10 oos.writeObject(s2); 11 oos.flush(); 12 oos.close(); 13 14 FileInputStream fis=new FileInputStream("one.obj"); 15 ObjectInputStream ois=new ObjectInputStream(fis); 16 s1=(SeriableSingleton) ois.readObject(); 17 ois.close(); 18 19 System.out.println(s1 == s2); 20 21 } catch (Exception e){ 22 23 } 24 } 25 }
显示结果是false.
这个问题很好办,加一行代码的事情。
1 public class SeriableSingleton implements Serializable { 2 public final static SeriableSingleton one=new SeriableSingleton(); 3 4 private SeriableSingleton() { 5 } 6 7 public static SeriableSingleton getInstance(){ 8 return one; 9 } 10 11 private Object readResolve(){ 12 return one; 13 } 14 }
上面红色就是添加的一个方法。
因为JDK在readObject()时候,判断了有没有readResolve()方法,如果有的话就执行这个方法,没有就不执行了,我们充分利用了这个特点,给他直接返回了一个one对象;
所以就不执行实例化了。
原文地址:https://www.cnblogs.com/guoyansi19900907/p/12721676.html
- 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 数组属性和方法
- JVM调优调的是什么?是寂寞吗?
- 如何做攻击溯源自动化
- Java多线程相关知识点扩展实例分析
- 巧用 Nsenter 调取宿主机工具调试容器内程序
- 如何在 Kubernetes 集群中集成 Kata
- Pytest实战
- Native 与 Weex 交互通用解决方案
- 20个MySQL运维案例,请查收!
- Unity3D中使用Joystick Pack实现摇杆控制
- Unity3D使用Timeline实现过场动画
- Oracle中ascii为0的陷阱
- VBA解析VBAProject 05——提取模块代码
- VBA解析VBAProject 06——清除VBA工程密码
- VBA解析VBAProject 07——隐藏模块
- python测试开发django-83.Dockerfile部署django项目