单例模式Singleton 你是唯一
单例模式:确保指定类有且只有一个对象,并提供一个全局的访问点。
1.单例模式(分为懒汉和饿汉)
* 懒汉式,创建时判断对象实例为空,则创建
*
* @author k
*
*/
public class SingleA {
// 定义私有并静态的本类对象实例
private static SingleA mInstance;
// 私有化构造函数
private SingleA() {
}
// 静态方法返回对象实例
public static SingleA newInstance() {
if (mInstance == null) {
mInstance = new SingleA();
}
return mInstance;
}
}
/***
* 饿汉式单例模式
*
* @author k
*
*/
public class SingleB {
// 定义私有并静态的本类对象实例,并初始化
private static SingleB mInstance = new SingleB();
// 私有化构造函数
private SingleB() {
}
// 静态方法返回对象实例
public static SingleB newInstance() {
return mInstance;
}
}
上面两种方式的实现单例模式,总结:
1. 私有化构造函数
2. 定义私有并静态的类对象
3. 提供静态方法返回该对象
多线程下单例模式
懒汉模式中,每次newIntance创建时,会先判断对象是否为空,如果为空则会通过调用私有构造函数实例化对象并赋值。那么问题来了,如果在多线程中通过newIntance创建对象时,可能就出现线程1判断mInstance=null,在实例化对象并赋值之前,线程2也开始newIntance,线程2中 判断mInstance=null,则再次实例化对象并赋值,这种方式是线程不安全的,不满足单例模式有且只有一个对象的原则。
测试结果如下:
// 静态方法返回对象实例
public static SingleA newInstance() {
if (mInstance == null) {
mInstance = new SingleA();
}
return mInstance;
}
/***
* 多线程单例实例化测试
*/
private static void multipleThreadSingletonTest() {
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
public void run() {
SingleA a = SingleA.newInstance();
System.out.println(a);
}
}).start();
new Thread(new Runnable() {
public void run() {
SingleA a = SingleA.newInstance();
System.out.println(a);
}
}).start();
}
}
观察打印结果:发现不止创建了一个对象
解决问题多线程不安全,采用线程同步锁即可解决该问题,改造后代码如下
// 静态方法返回对象实例,方法上synchronized同步锁
public static synchronized SingleC newInstance() {
if (mInstance == null) {
mInstance = new SingleC();
}
return mInstance;
}
测试发现newInstance方法上加上synchronized同步方法后,可以多线程下创建对象有且只有一个
缺点是:每次调用newInstance方法都进行同步,造成了不必要的开销,性能下降。
Double CheckLock单例模式
先判断实例对象是否为空,避免不必要的同步
在实例为null的情况下创建实例,使用synchronized同步,避免多线程不安全的问题
// Double CheckLock单例模式
public static SingleD newInstance() {
if (mInstance == null) {// 先判断实例对象是否为空,避免不必要的同步
synchronized (SingleD.class) {
if (mInstance != null) {
mInstance = new SingleD();
}
}
}
return mInstance;
}
静态内部类单例模式
上面Double CheckLock单例模式,可以解决多线程同步,性能问题,但小概率范围内也可能出现实例化失效的问题。
采用静态内部类的方式实现单例模式,看上去似乎更像“饿汉式”模式,实际上当第一次加载SingleE类是不会初始化mInstance,只有在第一次调用newInstance()方法是才会初始化,也就是只有在第一次调用newInstance()虚拟机才加载静态内部类SingleHoloder,这种方式线程安全,能保证对象的唯一性,推荐使用。
/***
* 静态内部类单例模式
*
* @author k
*
*/
public class SingleE {
// 私有化构造函数
private SingleE() {
}
// 静态内部类 创建
public static class SingleHoloder {
private static final SingleE mInstance = new SingleE();
}
public static SingleE newInstance() {
return SingleHoloder.mInstance;
}
}