Singleton(单例模式)
描述
任何情况绝对只有一个实例,并提供一个全局访问点。
场景
单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型。J2EE 标准中的 ServletContext 和 ServletContextConfig、Spring 框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。
图解
写法
饿汉模式
基础
/**
* 优点:执行效率高,性能高,没有任何的锁
* 缺点:某些情况下,可能会造成内存浪费
*/
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
静态代码块
public class HungryStaticSingleton {
//先静态后动态
//先上,后下
//先属性后方法
private static final HungryStaticSingleton hungrySingleton;
//装个B
static {
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){}
public static HungryStaticSingleton 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 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 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 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();
}
}
破坏单例模式方法及解决
反射方式
public class ReflectTest {
public static void main(String[] args) {
try {
Class<?> clazz = LazyStaticInnerClassSingleton.class;
Constructor c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
Object instance1 = c.newInstance();
Object instance2 = c.newInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance1 == instance2);
}catch (Exception e){
e.printStackTrace();
}
}
}
通过构造方法解决
private LazyStaticInnerClassSingleton(){
if(LazyHolder.INSTANCE != null){
throw new RuntimeException("不允许非法访问");
}
}
序列化
public class SeriableSingletonTest {
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过实现readResolve方法解决
private Object readResolve(){ return INSTANCE;}
其他
Java
反序列化 可以通过实现readResolve自定义反序列化对象的创建过程。
volatile 保证变量在多线程中的可见性。通过字节码建立内存屏障,解决指令重排序问题。
ThreadLocal 线程局部变量,不同线程间不可见。