java设计模式之单例模式
一、概述
1、概念: 是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点;隐藏其所有的构造方法;属于创建型模式 2、使用场景: 确保任何情况下都绝对只有一个实例(ServletContext、ServletConfig、ApplicationContext、DBPool) 3、优点: a.在内存中只有一个实例,减少了内存开销 b.可以避免对资源多重占用 c.设置全局访问点,严格控制访问 4、缺点: a.没有接口,扩展困难 b.如果要扩展单例对象,只有修改代码,没有其他途径
二、实现方式
1、饿汉式
/**
* 描述:饿汉式单例 - 静态属性
* @author yys
*/
public class HungrySingleton {
// 私有构造,防止外界new对象
private HungrySingleton() {}
private static final HungrySingleton hungrySingleton = new HungrySingleton();
public static HungrySingleton getHungrySingleton() {
return hungrySingleton;
}
}
/**
* 描述:饿汉式单例 - 静态代码块
* @author yys
*/
public class HungryStaticSingleton {
// 私有构造,防止外界new对象
private HungryStaticSingleton() {}
private static final HungryStaticSingleton hungryStaticSingleton;
// 静态代码块赋值
static {
hungryStaticSingleton = new HungryStaticSingleton();
}
public static HungryStaticSingleton getHungryStaticSingleton() {
return hungryStaticSingleton;
}
// 普及下加载顺序
// 先静态、后动态 / 先属性、后方法 / 自上而下
}
// 总结:饿汉式单例是在类加载的时就立即初始化,并且创建单例对象。绝对线程安全,在线程还没出现以前就完成实例化,不存在访问安全问题。
// 优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
// 缺点:浪费内存空间,类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能占着茅坑不拉屎。
2、懒汉式
/**
* 描述:懒汉式单例 - 同步方法
* 优点:线程安全
* 缺点:(性能低) 因本质为同步方法,再线程多的情况下,CPU压力上升,导致大批线程阻塞,导致性能下降
* @author yys
*/
public class LazySimpleSingleton {
// 私有构造,防止外界new对象
private LazySimpleSingleton() {}
private static LazySimpleSingleton lasy;
// 同步方法,多线程环境下,线程安全,but:线程多的情况下,CPU压力上升,导致大批线程阻塞,导致性能下降
public synchronized static LazySimpleSingleton getInstance() {
if(null == lasy) {
lasy = new LazySimpleSingleton();
}
return lasy;
}
}
/**
* 描述:懒汉式单例 - 双重检查锁
* 优点:线程安全,(性能较高) 因本质为同步代码块,较同步方法的方式来说,性能大幅度提升
* 缺点:用到synchronized关键字,性能还是存在影响。
* @author yys
*/
public class LazyDoubleCheckSingleton {
// 私有构造,防止外界new对象
private LazyDoubleCheckSingleton() {}
// volatile保证可见性和禁止指令重排序
private volatile static LazyDoubleCheckSingleton lasy;
public static LazyDoubleCheckSingleton getInstance() {
// 第一重检查锁
if(null == lasy) {
synchronized (LazyDoubleCheckSingleton.class) {
// 第二重检查锁
if(null == lasy) {
lasy = new LazyDoubleCheckSingleton();
}
}
}
return lasy;
}
// 创建对象的顺序:
// 1、分配对象的内存空间
// 2、初始化对象
// 3、设置lasy指向刚分配的内存地址
// 为什么需要volatile:防止JVM指令重排序
}
/**
* 描述:懒汉式单例 - 静态内部类
* 优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
* 缺点:通过反射机制可以破坏单例
*
* 反射机制破坏单例解决方案:在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常
*
* @author yys
*/
public class LazyInnerClassSingleton {
// 私有构造,防止外界new对象
private LazyInnerClassSingleton() {
// 防止反射机制破坏单例
if(null != LazyHolder.LAZY) {
throw new RuntimeException("不允许通过反射机制创建多个实例");
}
}
// 关键字作用:static 是为了使单例的空间共享 / final 保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance() {
// 在结果返回以前,会先加载内部类
return LazyHolder.LAZY;
}
// 默认不加载
private static class LazyHolder {
// 这里其实是饿汉式单例
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
// 总结:被外部类调用时才会加载,创建实例。
3、登记式单例
/**
* 描述:登记式单例
* 枚举式单例
* @author yys
*/
public enum EnumSingleton {
// 本质为饿汉式单例的实现
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
/**
* 描述:登记式单例
* 容器式单例
* @author yys
*/
public class ContainerSingleton {
// 私有构造,防止外界new对象
private ContainerSingleton() {}
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>(0);
private static Object getBean(String className) {
synchronized (ioc) {
if(!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}else {
return ioc.get(className);
}
}
}
/**
* 记录: spring ioc 容器位置
* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
*
* 159行
* private final ConcurrentMap<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();
*/
}
// 总结:将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
扫描二维码关注公众号,回复:
10813361 查看本文章
4、ThreadLocal 线程单例
/**
* 描述:ThreadLocal 线程单例
* @author yys
*/
public class ThreadLocalSingleton {
// 私有构造,防止外界new对象
private ThreadLocalSingleton() {}
// ThreadLocal 不能保证其创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
// 总结:不能保证其创建的对象是全局唯一,保证线程内部的全局唯一,且天生线程安全
Now ~ ~ ~写到这里,就写完了,如果有幸帮助到你,请记得关注我,共同一起见证我们的成长。