一. 类型
创建型模式
二. 优点
- 保证一个类仅有一个实例,减少了内存开销.
- 可以避免对资源的多重占用.
- 设置全局访问点, 严格控制访问.
三. 缺点
- 没有接口, 扩展困难.
- 如果要扩展单例对象, 只有修改代码, 无其他途径.
四. 单例的相关注意点
- 私有化构造器
- 保证线程安全
- 延迟加载
- 防止序列化和反序列化破坏单例
- 防御反射破攻击单例
五. 多种单例写法
饿汉式单例
/**
* 优点:执行效率高,性能高,没有任何的锁
* 缺点:由于提前创建对象, 没有使用的情况下,可能会造成内存浪费
*/
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
懒汉式单例(延迟加载)
简单写法
/**
* 优点:节省了内存,线程安全
* 缺点:性能低
*/
public class LazySimpleSingletion {
private static LazySimpleSingletion instance;
private LazySimpleSingletion(){}
public synchronized static LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
DCL 双重检查锁写法
/**
* 优点:性能高了,线程安全了
* 缺点:可读性难度加大,不够优雅
*/
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
//检查是否要阻塞
if (instance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
//检查是否要重新创建实例
if (instance == null) {
instance = new LazyDoubleCheckSingleton();
//指令重排序的问题
}
}
}
return instance;
}
}
静态内部类写法
/**
静态内部类写法
ClassPath : LazyStaticInnerClassSingleton.class
LazyStaticInnerClassSingleton$LazyHolder.class
优点:利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
缺点:不优雅
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){
// 此处防止反射攻击破坏单例所做的检查, 此处让代码变得不优雅, 且不易懂
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");
}
}
private static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.INSTANCE;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
注册式单例
枚举式单例
/**
* 枚举式单例
* 优点: 优雅的写法
* 缺点: 不能大批量创建对象
*/
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;
}
}
容器式单例
/**
* 容器式单例
* 基于枚举式单例的改进, 可以大批量创建对象,
* 以下代码需要增加线程安全的逻辑
*/
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getInstance(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);
}
}
}
解决序列化的单例
public class SeriableSingleton implements Serializable {
//序列化
//把内存中对象的状态转换为字节码的形式
//把字节码通过IO输出流,写到磁盘上
//永久保存下来,持久化
//反序列化
//将持久化的字节码内容,通过IO输入流读到内存中来
//转化成一个Java对象
public final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance(){
return INSTANCE;
}
// 反序列化时, 输入流对象底层实际上是new的一个新对象返回
// 如果有该方法的话, 输入流会通过反射找到该名称的方法, 获取到返回结果来避免new一个对象
private Object readResolve(){
return INSTANCE;
}
}
ThreadLoocal 式单例
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocaLInstance.get();
}
}