一 定义
单例模式(Singleton Pattern):是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏其所有的构造方法
属于创建性模式
二 单例模式的几种实现方式
(一)饿汉式单例:在单例类首次加载时就创建单例
public class HungrySingleton {
private static final HungrySingleton hungrySingletonm = new HungrySingleton();
private HungrySingleton(){}
public HungrySingleton getInstance(){
return hungrySingletonm;
}
}
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingletonm;
private HungryStaticSingleton(){}
static {
hungrySingletonm = new HungryStaticSingleton();
}
public HungryStaticSingleton getInstance(){
return hungrySingletonm;
}
}
上面两种方式没有本质的区别,都是在类加载时就创建类的实例
优点:执行效率高,性能高,没有任何的锁
缺点:某种情况下,可能会造成内存浪费
(二)懒汉式单例:被外部调用时才创建实例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
A方式 优点:不是加载类的时候就被创建,可以节约内存空间
缺点:不是线程安全的,多线程情况下,会产生多个实例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
B方式 优点:节约了内存空间,并且是线程安全的
缺点:性能很低
public class LazySingleton {
private volatile static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance == null){ //第一层检查:检查是否阻塞
synchronized(LazySingleton.class){
if(instance == null){ //第二层检查:检查是否要重新创建实例
instance = new LazySingleton(); //这有一个指令重排序的问题,上面的变量上加上volatile字段
}
}
}
return instance;
}
}
C方式 双重检查,比较好的一种写法
优点:性能提高了,线程安全了
缺点:可读性不好,不够优雅
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){
if(Holder.INSTANCE != null){
throw new RuntimeException("不允许非法访问"); //防止反射破坏单例
}
}
public static StaticInnerClassSingleton getInstance(){
return Holder.INSTANCE;
}
private static class Holder{
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}
D方式 写法优雅,利用了java本身的语言特性,性能高,避免了内存浪费
上面的几种写法都会被反射和序列化破坏单例
(三)注册式单例:将每一个实例都缓存到统一的容器中,使用唯一标示获取实例
public enum EnumSingleton { INSTANCE; public static EnumSingleton getInstance(){ return INSTANCE; } }
枚举式单例可避免序列化破坏和反射破坏
public class ContainerSingleton { private ContainerSingleton(){} private static Map<String,Object> ioc=new ConcurrentHashMap<String,Object>(); public static Object getBean(String className){ Object instance = null; if(!ioc.containsKey(className)){ try { instance = Class.forName(className).newInstance(); ioc.put(className,instance); } catch (Exception e) { e.printStackTrace(); } return instance; }else{ return ioc.get(className); } } }
容器式单例适用于创建实例非常多的情况,便于管理。但是,是非线程安全的。
(四)ThreadLocal单例:保证线程内部的全局唯一,且天生线程安全。严格上说不能算作是一种单例模式,只是保证了每个线程里的实例是一个
public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalSIngleton = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton(){} public ThreadLocalSingleton getInstance(){ return threadLocalSIngleton.get(); } }
三 学习单例模式的重点
1. 私有化构造器
2. 保证线程安全
3. 延迟加载
4. 防止序列化和反序列话破坏单例
5. 防御反射攻击单例
浙公网安备 33010602011771号