单例模式
- 是否线程安全
- 是否延迟加载
懒汉式 ,线程不安全
- 是否延迟加载: 是
- 是否线程安全: 否
class SingleTonOne{
private static SingleTonOne instance ;
public SingleTonOne() {
}
public static SingleTonOne getInstance() {
if (instance == null) {
instance = new SingleTonOne();
}
return instance;
}
}
懒汉式,线程安全
- 是否延迟加载: 是
- 是否线程安全: 是
class SingleTonTwo {
private static SingleTonTwo instance ;
public SingleTonTwo() {
}
public static synchronized SingleTonTwo getInstance() {
if (instance == null) {
instance = new SingleTonTwo();
}
return instance;
}
}
饿汉式,线程安全
- 是否延迟加载: 否
- 是否线程安全: 是
class HungrySingleTon{
//只要类被加载 instance就会被实例化,因此没有延迟加载的特性
private static HungrySingleTon instance = new HungrySingleTon();
public HungrySingleTon() {
}
public static HungrySingleTon getInstance() {
return instance;
}
}
双重锁/双重校验锁
- 是否延迟加载: 是
- 是否线程安全: 是
class DoubleCheckSingleTon{
//volatile 保证变量的可见性,指示JVM,这个变量是共享且不稳定的,每次使用它时都需要到主存中进行读取
//volatile有两个作用:1.保证变量的可见性 2.禁止jvm的指令重排序
private volatile static DoubleCheckSingleTon instance = null;
public DoubleCheckSingleTon() {
}
public static DoubleCheckSingleTon getInstance() {
if (instance != null) {
return instance;
}
synchronized(DoubleCheckSingleTon.class) {
if (instance == null) {
instance = new DoubleCheckSingleTon();
/** instance = new DoubleCheckSingleTon();
* 这块代码实际是分为三步:
* 1. 为 instance 对象分配内存空间
* 2. 初始化 instance
* 3.将 instance指向分配的内存地址
*
* 由于JVM具有指令重排的特性,执行顺序可能是 1-> 3 -> 2 。
* 指令重排在单线程下不会出现问题,
* 但是在多线程环境下会导致一个线程获得还没有初始化的实例
*/
}
}
return instance;
}
}
登记式/静态内部类
- 是否延迟加载: 是
- 是否线程安全: 是
class StaticInnerClass {
private static class SingleTonHolder{
//StaticInnerClass 被加载的时候, instance不会被实例化 ,
// 只有显示调用 getInstance 时才会显式装载 SingleTonHolder,此时才会实例化,因此也实现了延迟加载
private static final StaticInnerClass INSTANCE = new StaticInnerClass();
}
public StaticInnerClass() {
}
public static StaticInnerClass getInstance(){
return SingleTonHolder.INSTANCE;
}
}
枚举
enum EnumSingleton{
INSTANCE;
}
枚举能防止反射,反射的时候会检查修饰符
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
防止反序列化:
在序列化的时候Java仅仅是将枚举对象的name属性输到结果中,
反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。也就是说,序列化的时候只将INSTANCE这个名称输出,反序列化的时候再通过这个名称,查找对应的枚举类型
因此反序列化后的实例也会和之前被序列化的对象实例相同