Java单例模式的几种实现方式

什么是单例?

单例类在整个程序中只能由一个实例,这个类负责创建自己的对象,并保证只能有一个对象被创建

代码实现的要点:
1)私有构造器
2)持有该类的属性
3)对外提供获取实例的静态方法
下面为Java单例的几种实现形式
(1)饿汉式
类加载的时候就创建实例
private static Singleton1 instance=new Singleton1();//类加载时候就创建了

private  Singleton1() {//私有化构造器
		System.out.println("Singleton1 loaded");
	}
	public static Singleton1 getInstance() {//获取实例
		return instance;
	}
线程安全 反射不安全 反序列化不安全  在类加载的时候就被初始化,占浪费空间
但是这时候反序列化是不安全的,因为 静态变量在序列化是不会被保存的,所以在反序列化的时候会重新生成一个实例,这时候需要加一个readResolve方法
public Object readResolve() {
		return instance;
	}

(2)登记式(静态内部类)

/*
 * 登记式
 */
public class Singleton2 {
	
	private static class SingletonHolder{//定义一个私有静态内部类
		private static Singleton2 instance=new Singleton2();//在静态私有内部类定义一个私有静态方法
	}
	private Singleton2() {
		System.out.println("Singleton2 loaded");
		if (SingletonHolder.instance!=null) {//防止通过反射来new实例
			throw new IllegalStateException();
		}
	}
	public static Singleton2 getInstance() {
		return SingletonHolder.instance;
	}
}

线程安全、防止反射攻击、反序列化不安全
(3)枚举式

public Singleton3{
	INSTANCE{
		@override
		protected void doSomething(){
			System.out.println("doSomething");
		}
	};
	private abstract void doSomething();
}//该枚举类型反编译后的得到一个abstract的类,abstract的类无法通过反射实例化
线程安全、立即初始化 自动支持序列化 防止反序列化创建对象  直接防止反射攻击 更简单高效,但是不适合继承的类

(4)懒汉式
相对于饿汉式的区别,在使用的时候再去加载对象,节省资源

/*
 * 懒汉式
 */
public class Singleton4 {

	private static Singleton4 instance=null;
	private Singleton4() {
		
	}
	public static Singleton4 getInstance() {
		if (instance==null) {
			instance=new Singleton4();
		}
		return instance;
	
	}
}

如果整个时候又多个线程访问这个实例,但是这个实例不存在,每个线程都会进入new多个实例,这个时候线程是不安全的,我们应该在该方法加上synchronized关键字来加同步锁实现线程同步,即
//同步方法
	public static  synchronized Singleton5  getInstance() {
		if (instance==null) {
			instance=new Singleton5();
			
			
		}
		return instance;
	}

或者同步类

public static   Singleton6  getInstance() {
		synchronized (Singleton6.class) {//同步代码块
			if (instance==null) {
				instance=new Singleton6();
				
				
			}
		}
		return instance;
	}

然而,加synchronized(同步锁)则大大增加了系统开销

(5)双检索
减小了系统开销 ,相对于懒汉式,加if(instance==null){},即,只是一开始单例初始化的时候同步


public class Singleton7 {
	private static Singleton7 instance=null;
	private Singleton7() {
		
	}
	public static   Singleton7  getInstance() {
		if (instance==null) {//双检索,只是一开始单例初始化的时候同步
			synchronized (Singleton7.class) {//同步代码块
				if (instance==null) {
					instance=new Singleton7();
				}
			}
		}
		return instance;
	}
}

但是由于编译器自身的缘故,可能会出现指令重排,这时候需要在私有的属性前面加volatile关键字

private static volatile Singleton7 instance=null;

关于指令重排
,CPU和编译器为了提升程序的执行效率, 通常会按照一定的规则对指令进行优化, 如果两条指令互不依赖, 有可能它们执行的顺序并不是源代码编写的顺序。
比如instance=new Singleton7()会执行以下操作
1)分配对象内存空间
2)初始化对象
3)instance指向(1)中分配的空间
在某些编译器中,可能出现指令重排
1)分配对象内存空间
2)instance指向(1)中分配的空间(但是此时没有初始化对象)
3)初始化对象
比如又一个线程通过了第一个if语句通过了synchronized拿到锁进入了第二个if里面实例化了方法,分配了对象,但是这时候instance却没有初始化,这时候如果又有一个线程第一个if为false,直接返回了instance,而此时的instance却是没有初始化的实例,这时候就出问题了
volatile作用

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排
    通过加volatile就禁止指令重排,保证cpu按照顺序指向指令,就不会担心以上问题了

(6)ThreadLocal

package singleton;
/*
 * ThreadLocal保证一个线程之间是单例
 */
public class Singleton8 {
	private static Singleton8 instance=null;
	private Singleton8() {
		
	}
	private static final ThreadLocal<Singleton8> threadLocalSingleton=new ThreadLocal<Singleton8>() {
		@Override
		protected Singleton8 initialValue() {
			return new Singleton8();
		}
	};
	public static Singleton8 getInstance() {
		return threadLocalSingleton.get();
	}
}

不加锁,以空间换时间 ,为每个线程提供变量的独立副本,可以保证各自线程中是单例的,但是不同线程之间不是
(7)CAS(Compare And Swap)

public class Singleton9 {
	private static final AtomicReference<Singleton9> instance=new AtomicReference<Singleton9>();
	private Singleton9() {
		System.out.println("Singleton9 loaded");
	}
	public static final Singleton9 getInstance() {
		for(;;) {
			Singleton9 current=instance.get();
			if (current!=null) {
				return current;
				
			}
			current=new Singleton9();
			if (instance.compareAndSet(null, current)) {
				return current;
			}
		}
		
		
		
	}
}

比较交换技术 假设线程访问资源没有出现冲突,如果出现冲突就重试当前策略 无锁乐观策略,线程安全,实现比较复杂

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值