由单例模式引发的一系列思考

一、单例模式

饿汉模式

饿汉模式:在第一次引用该类便创建对象,而忽略是否使用该对象,最好的方式应当是延迟加载

    class csingleton {
    	public csingleton() { }
    	public static csingleton sing = new csingleton(); 
		public csingleton getInstance() {
	 	 	return sing; 
	  	}

懒汉式

优势:延迟加载,减小负载
缺点:只能适用于单线程模式

public static csingleton sing ; 
/**
	 * 单线程模式
	 * @return
	 */
	public csingleton getInstanceThread() {
		if(sing == null) {
			return new csingleton();
		}
		return sing;
	}

二、如果是多线程如何使用单例模式?

锁synchronized字段

synchronied 优势:保证了多线程的安全
缺点:多个线程频繁调用就会导致程序性能下降

/**
	 * 多线程模式
	 * @return
	 */
	public synchronized csingleton getInstanceMulThread() {
		if (sing == null) {
			return new csingleton();
		}
		return sing;
	}

细粒度锁

细粒度锁:保证了只有在对象为空的时候才会实例化
缺点:假设有两个线程A、B同时竞争锁,线程A和线程B同时判断实例对象为空,当线程A在实例化对象之后,线程B又去实例化对象,没有解决线程同步问题

/**
	 * 多线程模式,锁块
	 * 
	 * @return
	 */
	public csingleton getInstanceMulThreadblock() {
		if (sing == null) {
			synchronized (csingleton.class) {
				return new csingleton();
			}
		}
		return sing;
	}

引出貌似完美的双重检查锁(DCL)

首先分析一下双重检查锁中每一行的含义:
1、判断是否初始化对象实例,没有初始化则需要获取所,初始化对象,已经初始化则直接返回
2、获取锁,锁住类对象
3、可能存在线程A和线程B同时进入1之后的代码块,当线程A实例化对象后释放锁,线程B在获取锁之后,需要再次判断对象是否实例化
4、实例化对象

/**
	 * 双重检查锁
	 * @return
	 */
	public csingleton DCL() {
		if (sing == null) {//1
			synchronized (csingleton.class) {//2
				if (sing == null) {//3
					return new csingleton();//4
				}
			}
		}
		return sing;
	}

一、问题代码行return new csingleton();//4
创建一个对象的代码可以分解为3行位代码:
(1)memory = allocate() //分配内存
(2)ctorInstance(memory)//初始化对象
(3)instance = memory//设置instance指向刚分配的内存地址

(2)、(3)之间没有数据依赖,因此会导致重排序,重排序导致的问题就是:当线程A首先为将instance指向内存空间,但其实并未初始化,线程B开始判断instance是否为空,此时虽然instance不为空,但是该对象并未初始化

三、如何解决DCL存在的问题?

volatile

volatile第二个特点:禁止指令重拍

class singletonImp {
	public singletonImp() {
	}

	public static volatile singletonImp singleton;

	/**
	 * 双重检查锁,volatile改进:禁止指令重排
	 * 
	 * @return
	 */
	public singletonImp DCL() {
		if (singleton == null) {
			synchronized (csingleton.class) {
				if (singleton == null) {
					return new singletonImp();
				}
			}
		}
		return singleton;
	}
}

内部静态类

volatile的思路是禁止指令重拍,内部静态类的思路就是允许指令重拍,但是不会给其他线程看到
JVM中每一个线程会至少获得一次锁来保证这个类已经初始化,通过锁可以同步多个线程对同一类的初始化

假定线程A和线程B开始竞争:
一、线程A获得初始化锁,线程A在发现state= noinitialization时候,开始初始化,最后释放锁
二、线程B竞争失败,state= initialing在初始化锁的condition上阻塞,等待唤醒
三、线程在获得初始化锁的时候,state = initilized,之后唤醒阻塞线程,释放初始化锁,初始化完成
四、线程B唤醒后,判断state=initilized,已经初始化,之后释放初始化锁,初始化完成
五、线程C获取初始化锁,判断state=initilized,已经初始化,之后释放初始化锁,初始化完成

class singletonImp2 {
	/**
	 * 内部静态类解决,内部静态类只初始化一次
	 * 
	 * @author 12803
	 *
	 */
	private static class InnerSingleton {
		private static singletonImp2 singleton = new singletonImp2();
	}

	public singletonImp2 getInstance() {
		return InnerSingleton.singleton;
	}
}

此时已经能达到多线程的同步,OVER

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值