单例模式分为饿汉式和懒汉式
饿汉式有两种实现方法
1.第一种正常饿汉式,在类加载的时候,就创建实例化对象,由于加载类时,天然就是线程安全,但是这种饿汉式不能防止反射破坏单例模式,也不能防止序列化破坏单例,unsafe也无法解决。
/*
* 测试饿汉式单例模式
*/
public class SingletonDemo1 {
//类初始化时 立即加载 创建实例化对象 由于加载类时,天然的是线程安全的
private static SingletonDemo1 instance=new SingletonDemo1();
SingletonDemo1() {
}
//方法没有同步,调用效率高
public static SingletonDemo1 getInstance() {
return instance;
}
}
在这种方法上有一种优化可以防止序列化破坏单例,也能防止反射破坏单例模式
public class SingletonDemo1 implements Serializable{
//类初始化时 立即加载 由于加载类时,天然的是线程安全的
private static SingletonDemo6 instance=new SingletonDemo6();
SingletonDemo6() {
if(instance!=null) { //构造方法抛出异常是防止反射破坏单例
throw new RuntimeException();
}
}
//方法没有同步,调用效率高
public static SingletonDemo6 getInstance() {
return instance;
}
public Object readResolve() { // 防止序列化破坏单例
return instance;
}
}
2.,枚举实现,枚举元素本身就是单例对象,延迟加载 , 没有延迟加载,反射和反序列化不可以 破解,只有unsafe无法解决。
public enum SingletonDemo5 {
//这个枚举元素 本身就是单例对象
INSTANCE;
//添加自己需要的操作
private void singletonOperation() {
}
}
懒汉式有3种实现方法
1.懒汉式单例模式,延迟加载,其实只需要在未创建实例对象时同步,但是这种方法在调用的时候每次都会调用同步,导致调用效率低。
public class SingletonDemo2 {
//类初始化时 不初始化这个对象 延时加载,用的时候在创建
private static SingletonDemo2 instance;
private SingletonDemo2() {
}
//多线程 方法同步,调用效率低
//其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步
public static synchronized SingletonDemo2 getInstance() {
if(instance==null) {
instance=new SingletonDemo2();
}
return instance;
}
}
2.双重检测锁来实现懒汉式,这个方法是在未创建对象时,进行同步。
但还是有一个问题,当对象未创建时,同时多个线程执行此方法,先进入一个线程进入同步方法,JVM虚拟机对于多线程的执行很可能存在指令重排的现象,因为最里面那层并不是原子操作,他仍需要分为3步,1.分配内存空间。2.执行构造放,初始化对象。3.把对象指向到内存空间。他可能会发生123,132顺序出现,导致返回的instance还没有执行构造方法就返回了,导致下个线程直接返回instance,但此时线程一并没有完成初始化对象。
解决方案:在类初始化上加一个volatile 来解决问题,volatile可以解决共享变量的可见性,有序性问题,但不能解决原子性问题。它可以使所有线程都能看到这个对象的值,能及时获取最新的值。
volatile 可以让共享变量在线程间实现可见,原因就在于在JVM的语义层面要求被volatile修饰的共享变量,在工作内存中的修改要立刻同步回主内存,并且读取也需要每次都重新从主内存中刷新一份到工作内存中后才可以操作。
public class SingletonDemo3{
//类初始化时 不初始化这个对象 延时加载,用的时候在创建
// volatile 解决共享变量的可见性,有序性问题,不能解决原子性
// volatile 相当于一个屏障 等上面代码全部运行结束 才能继续往下运行
private static volatile SingletonDemo3 instance=null;
private SingletonDemo3() {
}
//提高了效率 延迟加载
public static SingletonDemo3 getInstance() {
if(instance==null) {
synchronized(SingletonDemo3.class) {
if(instance==null) {
instance = new SingletonDemo3();//不是原子操作
// 1、分配内存空间
// 2、执行构造方法,初始化对象
// 3、把对象指向到内存空间
// 多线程:具体计算机执行时,可能存在指令重排,导致:123,132顺序的出
//现,导致返回数据还没有执行构造方法而直接返回,导致问题。
}
}
}
return instance;
}
}
3.静态内部类实现懒加载,这种方式:线程安全,调用效率高,并且实现了延迟加载。
- 由于内部静态类只会被加载一次,故该实现方式是线程安全的
- 类加载的初始化阶段是单线程的
所以说使用静态内部类实现懒汉式,他的线程安全由java虚拟机负责。
public class SingletonDemo4 {
private static class SingletonClassInstance{
private static final SingletonDemo4 instance =new SingletonDemo4();
}
private SingletonDemo4() {
}
//方法没有同步 调用效率高
public static SingletonDemo4 getInstance() {
return SingletonClassInstance.instance;
}
}