1、饿汉式
public class Hungry {
// 私有化构造方法
private Hungry () {}
// 初始化对象
private static Hungry hungry = new Hungry();
// 公开的获取对象的方法
public static Hungry getInstance() {
return hungry;
}
}
2、懒汉式
2.1 单线程版本
多线程不安全原因?
多线程情况下,多个线程同时执行到 if(lazyMan ==null)语句,创建多个引用对象。
public class LazyMan {
private LazyMan() {};
private static LazyMan lazyMan;
// 公开获取对象的方法
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
2.2 多线程版本
第一种:
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName());
};
private static LazyMan lazyMan;
// 公开获取对象的方法 直接加Synchronized影响性能
public synchronized static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
第二种:DCL
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName());
};
private volatile static LazyMan lazyMan; // volatile 避免指令重排
// 公开获取对象的方法
public static LazyMan getInstance() {
if (lazyMan == null) { // 第一次校验,只有第一次线程不安全
synchronized (LazyMan.class) {
if (lazyMan == null) { // 第二次校验
lazyMan = new LazyMan();// 不是原子性操作
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、对象指向空间 1/3/2
*
* @Explain 指令重排导致命令执行顺序是 1、3、2
* 当A线程执行指令3时,对象部分初始化,线程B进行第一次判断lazyMan != null,直接return:解决方式,使用volatile避免指令重排
* */
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 100000; i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
}
3、静态内部类??
public class Inner {
private Inner () {};
public static class InnerClass {
private static final Inner INNER = new Inner();
}
public static Inner getInner() {
return InnerClass.INNER;
}
}
以上三种可以被反射破坏:通过反射调用私有构造器创建对象
4、枚举 枚举单例不能被反射破坏
enum修饰的类编译之后其实是一个final类型,所以无法被继承
enum修饰的类,其实是Enum的子类
INSTANCE是静态且不可变的EnumSingle对象
public enum EnumSingle {
INSTANCE;
public void doing(){
System.out.println();
}
public static void main(String[] args) {
EnumSingle.INSTANCE.doing();
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(EnumSingle.INSTANCE);
}).start();
}
}
}
尝试使用反射破坏枚举单例
// 枚举本身是一个类
public enum EnumSingle {
INSTANCE;
public void doing() {
System.out.println();
}
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
System.out.println(instance1);
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor();
constructor.setAccessible(true);
EnumSingle newInstance = constructor.newInstance();
System.out.println(newInstance);
}
}
异常:没有无参构造
正常防止反射创建对象抛出的异常
通过javap -p 反编译class文件,两个参数的构造器
通过两个参数的构造器创建对象
public enum EnumSingle {
INSTANCE;
public void doing() {
System.out.println();
}
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
System.out.println(instance1);
Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle newInstance = constructor.newInstance();
System.out.println(newInstance);
}
}
抛出异常