什么是单例模式?
首先构造器私有 并且必须保证一个类只有一个实例
饿汉式
public class A{
private A(){}
private final static A a = new A();
private static A getA(){
return a;
}
}
优缺点
编程简单 但是无法延迟创建对象 浪费空间
懒汉式
见名知意 很懒 所以会延迟创建对象
单线程写法
public Class A{
private A(){}
private static A a;
public static A getA(){
if(a==null){
a = new A();
}
return a;
}
}
多线程写法 上面的单线程写法明显是线程不安全的 因为没有做任何操作 这样在并发的时候就不安全
public Class A{
private A(){}
private static volatile A a;
public static synchronized A getA(){
if(a==null){
a = new A();
}
return a;
}
}
饿汉式的好处在于可以延迟加载 但是要反复判断锁 效率低
双重检查锁
public Class A{
private A(){}
private volatile static A a;
public static getA(){
if(a==null){
synchronized(A.Class){
if(a==null){
a = new A();
}
}
return a;
}
}
}
常见问题:
为什么需要volatile?
因为new对象这个过程不是一个原子操作
1.先分配空间
2.执行构造方法初始化对象
3.再把对象指向这个空间
2 3 步是可能发生指令重排的 如果是单线程还好 如果是多线程 比如:A线程过来 先分配空间 然后发生指令重排 直接执行第三步 把对象指向这个空间(这个空间现在是一片虚无) 此时 线程B过来 发现这个对象!-=null(这个判空是外层的判空) 那么B就直接把这个虚无的对象返回去了。
两次非空判断目的?
第一次其实可以没有 它的作用是提高效率 避免多线程访问时每个线程都尝试获取锁
第二次是延迟加载 lazy的思想
静态内部类
双重检查锁还是有点麻烦
public Class A{
private A(){}
public static Class A1{
static A a = new a A();
}
private static A getA(){
return A1.a;
}
}
=================================
上述代码都会出现一个问题 就是拦不住反射获取你的私有字段 1反射会破坏单例结构
关于反射:的确可以在私有构造里面加上判断 if(singlon!=null) xxxx但是要是都用反射的getDeclaredConstructor也不行了 然后我们可以自己再写一个boolean值的判断 但是这个值也可以通过反射获取私有字段 来更改 总之 反射就是不安全
但是newInstance方法里面明确说了 如果是枚举 就抛异常
枚举:
enum Type{
A,B,C,D;
static int value;
public static int getValue() {
return value;
}
String type;
public String getType() {
return type;
}
}
在原有的基础上,添加了类方法和实例方法。我们把Type看做一个类,那么enum中静态的域和方法,都可以视作类方法。和我们调用普通的静态方法一样,这里调用类方法也是通过 Type.getValue()即可调用,访问类属性也是通过Type.value即可访问。
下面的是实例方法,也就是每个实例才能调用的方法。那么实例是什么呢?没错,就是A,B,C,D。所以我们调用实例方法,也就通过 Type.A.getType()来调用就可以了。
最后,对于某个实例而言,还可以实现自己的实例方法。再看下下面的代码:
关于枚举:
反编译后 发现public final class T extends Enum 里面的变量都是public static final 的 static类型的属性会在类加载的时候被初始化 而Java类加载和初始化的过程是线程安全的
Singleton.INSTANCE.doSomeThing即可调用