目录
静态变量方式(饿汉式)
静态代码块方式(饿汉式)
枚举方式(饿汉式)
线程不安全(懒汉式)
线程安全(懒汉式)
双重检查锁方式
静态内部类方式
单例模式存在的问题
源码分析:
枚举方式不会被破坏
单例设计模式分类两种:
饿汉式:类加载就会导致该单实例对象被创建(三种)。
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建(四种)。
public class Singleton {
//提供私有构造方法
private Singleton(){}
//提供静态成员变量
private static Singleton singleton = new Singleton();
//提供对外开放方法
public static Singleton getInstance(){
return singleton;
}
}
public class Singleton {
//提供私有构造方法
private Singleton(){}
//提供静态成员变量
private static Singleton singleton;
//静态代码块
static {
singleton = new Singleton();
}
//提供对外开放方法
public static Singleton getInstance(){
return singleton;
}
}
线程安全,唯一 一个不会被破坏的单例模式实现。
在用反射获得构造器后,使用newInstance创建对象会判断是否为枚举,是枚举报错。
//判断是否是枚举,如果是枚举的话,报、抛出异常
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
//抛出异常,不能通过反射创建枚举
throw new IllegalArgumentException
("Cannot reflectively create enum objects");
public enum Singleton {
SINGLETON;
}
public class Singleton {
//提供私有构造方法
private Singleton(){}
//提供静态成员变量
private static Singleton singleton;
//提供对外开放方法
public static Singleton getInstance(){
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
加了synchronized锁。
public class Singleton {
//提供私有构造方法
private Singleton(){}
//提供静态成员变量
private static Singleton singleton;
//提供对外开放方法
public static synchronized Singleton getInstance(){
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
public class Singleton {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
序列化底层也是反射,
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
解决办法:添加readResolve()方法,在反序列化时被反射调用
静态内部类方式
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
ObjectInputStream类
public final Object readObject() throws IOException, ClassNotFoundException{
...
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);//重点查看readObject0方法
.....
}
private Object readObject0(boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
...
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
Object rep = desc.invokeReadResolve(obj);
...
}
return obj;
}
public class Singleton {
//私有构造方法
private Singleton() {
/*
反射破解单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。
private Enum readEnum(boolean unshared) throws IOException {
......
//读readOrdinaryObject这块会创建单例,读readEnum不会
Enum result = null;
Class cl = desc.forClass();
if (cl != null) {
try {
@SuppressWarnings("unchecked")
Enum en = Enum.valueOf((Class)cl, name);
result = en;
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name + " does not exist in " +
cl).initCause(ex);
}
if (!unshared) {
handles.setObject(enumHandle, result);
}
}
}
枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,从而保证了枚举实例的唯一性。
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
......
//这里判断Modifier.ENUM是不是枚举修饰符,如果是就抛异常
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;
}
源码很了然,确实无法使用反射创建枚举实例,也就是说明了创建枚举实例只有编译器能够做到而已。
BeanFactory中bean后处理器的排序的比较器对象。
public static final AnnotationAwareOrderComparator INSTANCE =
new AnnotationAwareOrderComparator();