单例模式
初衷是为了使资源共享,只需要赋值或者初始化一次,大家都能重复利用
应用场景: Listener本身单例,日历Calendar,IOC容器,配置信息Config(除了差异性的)
技术方案: 保证整个运行过程中只有一份,
饿汉式、懒汉式、注册登记式(枚举式)、反序列如何保证单例
1.饿汉式
特点:
- 这个是线程绝对安全的:
- 优点:没有加任何锁,执行效率高
- 用户体验:比懒汉式更好
- 缺点:类加载的时候就初始化了,不管用不用都占用空间,浪费内存
public class Hungry{
private Hungry{}
private static final Hungry hugry = new Hungry();
public static Hungry getInstance(){
return hugry;
}
}
2.懒汉式
特点: 延迟加载
2.1 (线程非安全)
public class Lazy{
private Lazy{}
private static Lazy lazy;
public static Lazy getInstance(){
if(lazy == null){
lazy = new Lazy();
}
return hugry;
}
}
2.2 (线程安全)
public class LazyTwo{
private LazyTwo{}
private static LazyTwo lazy;
public static synchronized LazyTwo getInstance(){
if(lazy == null){
lazy = new LazyTwo();
}
return lazy;
}
}
或者 //双重校验式,稍微增加点效率
public class LazyTwo{
private volatile static LazyTwo lazy; //这里这里需要使用volatile否则可能还是有问题
private LazyTwo() {}
public static LazyTwo getSingleton() {
if (lazy== null) {
synchronized (LazyTwo.class) {
if (lazy== null) {
lazy= new LazyTwo(); //如果lazy引用不是使用volatile修饰,这里会发生指令重排序的问题,造成一定的问题(虽然概率较小),可以参考https://blog.csdn.net/weixin_40792878/article/details/86411163
}
}
}
return singleton;
}
}
3.内部类实现
特点:
利用了外部类被调用的时候内部类才会被加载
内部类一定是要在方法调用之前初始化
兼顾了:懒汉式的延迟加载以及synchronized的安全问题
public class LazyThree{
private static boolean initialized =false; //这里可能出现通过反射改写的情况,也稍微耗费性能
private LazyThree(){
//避免反射入侵
sychronized(LazyThree.class){
if(initialized == false){
initialized = !initialized;
}else{
throw new RuntimeException("单例被侵犯...")
}
}
}
//static 使单例空间共享
//final保证方法不会被重写,重载
public static final LazyThree getInstance() {return LazyHolder.Lazy;}
//默认不加载
private static class LazyHolder{ //默认不加载
private static final LazyThree LAZY =new LazyThree();
}
}
4.注册登记式
这个是Spring中使用的方式,这里简单的使用下
public class BeanFactory{
private BeanFactory(){}
//利用ConcurrentHashMap的锁
private static Map<String,Object> ioc = new ConcurrentHashMap<>();
public static synchronized Object getBean(String className){
if(ioc.containsKey(className)){
return ioc.get(className);
}else{
Obj obj = Class.forName(className).newInstance();
return ioc.put(className,obj);
}
}
}
5.枚举式
public enum Season{
SPRING(){
private string des = "春天恒美";
},SUMMER;
}
6.使用CAS来实现
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
private Singleton() {}
public static Singleton getInstance() {
for (;;) {
Singleton singleton = INSTANCE.get();
if (null != singleton) {
return singleton;
}
singleton = new Singleton();
if (INSTANCE.compareAndSet(null, singleton)) {
return singleton;
}
}
}
}
7.解决反序列化单例问题解决
有时候反序列化 也会多new一个对象。
参考:https://blog.csdn.net/chy6575/article/details/51063505
public class Seriable implements Serializable{
private Seriable(){}
public final static Seriable INSTANCE = new Seriable();
public static Seriable getInstance(){
return INSTANCE;
}
//添加这个hook函数,那么系统在反序列化的过程中就会通过该Hook方法得到原有的单例
//而不是重新创建一个单例。
private Object readResolver(){
return INSTANCE;
}
}
这个只是一些笔记,反序列化和登记式只是一个方案,需要自己优化代码