聊聊如何实现更优雅的单例?
程序员的成长之路
共 8142字,需浏览 17分钟
·
2022-06-21 03:28
阅读本文大概需要 5 分钟。
来自:https://blog.csdn.net/weixin_43178828
前言
从最简单的开始 getInstance
你访问这个实例对象的时候 这个实例真的已经创建出来了吗? 这个实例对象是怎么创建出来的?创建的机制其实各有不同 比如所谓懒汉式的创建 是需要你 依赖他这个实例的时候 才会触发 从而创建单例的 为的是节省内存空间——没人用我创建他出来干嘛? 所以 在你获取对象之前 是不是有个触发的东西呢?说白了前边还有些代码逻辑实现懒汉式的思路 如果能够直接访问到这个实例对象 怎么保证是单例呢?我上次访问的和这次 是同一个对象? 如何保证创建的是单例 尤其是多线程的环境下
public class MySingleton {
private static MySingleton instance = new MySingleton();
private Singleton() {
}
public static MySingleton getInstance() {
return instance;
}
}
MySingleton singleton = MySingleton.getInstance();
复杂单例的创建过程——用串行化的static代码块解决
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
static{
helper = OtherSingleton.getInstance();
instance = MySingleton(helper);
}
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static MySingleton getInstance() {
return instance;
}
}
OtherSingleton
的getInstance
负责 我们这里最多加一层检查 拦截抛异常 ) 不会出现 因为多线程 导致创建的时候 helper拿不到的情况。。getInstance
里边也可以添加独特的东西(懒汉式我们后边再聊)懒汉式(延迟创建)
getInstance
时如果没有创建 则 开始创建 并返回单例 已经创建 则直接返回
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static MySingleton getInstance() {
if(instance == null)
instance = new MySingleton(OtherSingleton.getInstance());
return instance;
}
}
instance == null
当然进的来)内存泄漏?
解决方案:
synchronized
让整个代码块顺序执行 就类似static
代码块一样:public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null)
instance = new MySingleton(OtherSingleton.getInstance());
return instance;
}
}
更高的性能 double check locking
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null){
synchronized (MySingleton.class){
if(instance == null){
instance = new MySingleton(OtherSingleton.getInstance());
}
}
}
return instance;
}
}
MySingleton.class
对象 作为临界 意图明显 class
对象唯一 所以我们锁类 这样就保证了单线程的创建实例 其实和static
静态块异曲同工 因为static
也是类初始化的时候执行的 同样也是保证串行 绑定了class
对象的执行可见性 volatile
instance
!多线程在下一个指令周期 抢到了CPU计算的时间片 执行 那个时候缓存默认是不更新的volatile
他自然是个native的关键字 底层依赖C来实现变量的可见性!所以我们终极的程序应当是:public class MySingleton {
private volatile static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null){
synchronized (MySingleton.class){
if(instance == null){
instance = new MySingleton(OtherSingleton.getInstance());
}
}
}
return instance;
}
}
另一种懒汉式创建单例——静态内部类
static
来创建了 但是可不可能用另外一个类的static
来保证懒汉式单例呢?public class MySingleton {
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static class MySingletonAdapter {
private static final MySingleton instance = new MySingleton(OtherSingleton.getInstance());
}
public static getInstance() {
return MySingletonAdapter.instance;
}
}
Initialization on demand holder
序列化单例
Serializable
接口的单例 序列化倒没什么问题 但是反序列化时会产生新的实例对象 这里我们得改改readResolve()
方法 使得返回的实例保证单例public class MySingleton {
private static final long serialVersionUID = -3453453414141241L;
private static MySingleton instance = new MySingleton(OtherSingleton.getInstance());
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
private Object readResolve() {
return instance;
}
}
后记
OtherSingleton.getInstance()
但是有没有想过 系统刚开始一启动 实例化 谁先谁后呢?假设Other是后边才实例化的 前边的MySingleton
的创建不是吃瘪了嘛???OtherSingleton
初始化是需要MySingleton
的 怎么办呢?如果几百个bean
初始化的时候互相依赖 该怎么解决?invertion of controll
) 控制翻转思想 其实际实现是通过依赖注入dependency injection。
互联网初中高级大厂面试题(9个G) 内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈! ⬇戳阅读原文领取! 朕已阅
评论