一、单例模式
饿汉模式
饿汉模式:在第一次引用该类便创建对象,而忽略是否使用该对象,最好的方式应当是延迟加载
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;
}
}