单例模式
`
确保一个类在任何情况下都绝对只有一个实例,并提供全局访问点
隐藏其所有构造方法
属于创建型模式
例如: ServletContext ServletConfig ApplicationContext DBPool
基本单例模式:饿汉式单例、懒汉式单例、注册式单例、ThreadLocal单例
单例详解
1. 饿汉式单例
private、static、final修饰类的实例,private私有构造方法,
public、static修饰实例返回方法(GetInstance)
类一加载便创建实例
优点:执行效率高
缺点:内存浪费
类加载顺序:先静态后动态,先上后下,先属性后方法
private static final HungryStaticSingleton hungrySingLeton;
static{hungrySingLeton = new HungryStaicSingleton();}
2. 懒汉式单例
使用时才创建实例,不需要final修饰
优点:节省内存
缺点:线程不安全
private static LazySimpleSingleton instance;
public static LazySimpleSingleton GetInstance(){
if(instance == null){
instance = new LazySimpleSingleton();
}
return instance;
}
懒汉式线程不安全运行结果
a.返回实例结果相同
1、线程正常先后顺序执行;
2、后运行的线程覆盖之前的运行实例
b.返回实例结果不同
同时进入实例创建,第一个实例返回后,再创建第二个实例并返回
加锁保证懒汉式单例线程安全
synchronized
public synchronized static LazySimpleSingleton GetInstance(){
}
缺点:运行效率低
3. 双重检查懒汉式单例
public static LazyDoubleCheckSingleton GetInstance(){
//检查是否要阻塞
if(instance == null){
synchronized (LazyDoubleCheckSingleton .class){
//检查是否要重新创建实例
if(instance == null){
instance = new LazySimpleSingleton();
}
}
}
return instance;
}
指令重排序问题 需要在实例上添加volatile 关键字修饰,如下
`private volatile static LazyDoubleCheckSingleton instance;
缺点:程序可读性差,不优雅
4.静态内部类单例模式
LazyStaticInnerClassSingleton
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE =
new LazyStaticInnerClassSingleton();
}
优点:写法优雅,利用java本身语法特点(内部类被使用时才加载),性能高,避免内存浪费
缺点:能够被反射破坏
try{
Class<?> clazz = LazyStaticInnerClassSingleton.class'
Constructor c = clazz.getDeclaredConstrustor(null);
c.serAccessible(true);
Object instance = c.newInstance();
}catch(Exception e){
e.printStackTrace();
}
解决反射破坏,在构造方法中添加限制并抛出异常
private LazyStaticInnerClassSingleton(){
if(LazyHolder.INSTANCE != null){
throw new RuntimeException(“不允许非法访问”)
}
}
5.注册式单例(枚举单例)
EnumSingleton
public enum EnumSingleton(){
INSRANCE;
public static EnumSingleton GetInstance(){return INSTANCE;}
}
spring中枚举类,不能被反射实例化
缺点:内存浪费(同饿汉式单例)
6.spring IOC容器单例
ContainerSingleton
public class ContainerSingleton{
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getInstance (String className){
if(!ioc.containsKey(className)){
try{
ioc.put(className,Class,forName(className).newInstance())
}catch( Exception e){
e.printStackTrace();
}
}
return ioc.get(className);
}
}
线程不安全,序列化导致单例破坏
A单例输出为B文件,再将B文件读取为对象C,该对象C不等于A 从而导致单例被破坏
实现序列化接口的单例需要
重写方法,readResolve,直接返回该单例。
7.ThreadLocal单例
保证线程内部的全局唯一,且天生线程安全
public class ThreadLocalSingleton{
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue(){
return new ThreadLocalSingleton();
}
}
private ThreadLocalSingleton(){}
public stativ ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
线程内的单例
spring内的单例类应用场景
AbstractFactoryBean(双重检查懒汉式单例)
ErrorContext(ThreadLocal单例),隔开每一个线程的错误信息
总结
单例优点:减少内存开销,避免资源多重占用,设置全局访问点,严格控制访问
单例缺点:没有接口,扩展困难,只能通过修改源代码进行扩展
注意
私有化构造器
保证线程安全
延迟加载
防止序列化破坏单例
防御反射攻击单例