一:单例模式
所谓单例模式就是无论程序怎样运行,采用单例模式的类永远只有一个实例,需满足以下三个要求:
1.确保只有一个实例
2.其内部必须自己创建一个实例
3.必须为其他对象提供一个获取实例的方法
实现单例类的步骤:
1.单例类内部构造器私有化(用private修饰)
2.在内部new一个该类对象,并用private static修饰
3.提供一个public static 修饰的方法获取该实例
(饿汉式)示例代码
public class Single { private static Single single=new Single(); //内部的仅只一个的实例 private Single() { //构造器私有化 } public static Single getInstance(){ //给外部对象提供访问本类对象的静态方法 return single; } }
这是属于饿汉式的单例模式,因为是静态修饰的内部实例,单例类加载时,就会把内部实例加载完成也仅加载一次。
线程安全,多线程环境下不会有多个实例产生
效率不高,体现在单例类初始化阶段,因为要提前加载内部实例(静态变量会在类加载时加载),运行效率倒是挺快的,一般属于那种轻型项目会使用,因为小项目需要初始化加载的资源不大,整体效率就也不慢。
那么如果在项目中,那种大型的单例类又会怎样来实现呢?
这里推荐(懒汉式)
public class Single { private static Single single; //定义一个引用 private Single() { //构造器私有化 } public static Single getInstance() { if (single == null) single = new Single(); return single; } }
大型项目中,整体效率倒是提高了,但是在多线程环境下,却又可能产生多个实例(比如多个用户同时进入getInstance方法中,此时实例为null,所以会new多个实例)
那么又怎么解决呢?好办,加个synchronized
public class Single { private static Single single; private Single() { } public
synchronized
static Single getInstance() { if (single == null) single = new Single(); return single; } }
在多线程环境下倒是不会有线程安全问题了,但是多个用户访问这个实例方法时,该方法会被锁住。。
运行效率又大大降低了,怎么办呢!那就把锁的粒度降低
public class Single { private static Single single; private Single() { } public static Single getInstance() { if (single==null){ synchronized (Single.class){ if (single == null) single = new Single(); } } return single; } }
这种写法是只在加载实例的时候的进行同步,大大降低并发度,并且引入了一种新的设计机制“双重检查锁”
双重检查锁:
第一个判断是在检查实例存在之后,不必再进入同步代码块的机制;
第二个判断是在检查因还没有实例而同时进入同步代码块的线程确定实例是否被某个线程先一步创建了。
由此效率有了,线程又安全了,是不是就可以了呢。。还不是哦
假设有A、B两个线程,A线程先一步执行同步代码块的内容,发现还没有实例,于是创建实例,然而实例初始化是需要时间的,特别是那种大型单例类,但是这个实例的内存地址已经出来了,在实例还没有完成初始化的时候,B线程进入获取实例的方法了,它发现实例存在了,那好,那就直接返回实例给B呗,可是,这个实例还没有完全初始化完毕,肯定不行啊!这个办法怎么解决呢!用内部静态类(因为实例使用static修饰的,只有静态类能访问)
public class Single { private static Single single; private Single() { } private static class SingleHolder{ private static Single single=new Single(); } public synchronized static Single getInstance() { return SingleHolder.single; } }
因为java机制,内部静态类Singleholder只有在调用getInstance的时候才会加载,并且只加载一次!而且没有使用同步锁!
既保证了效率,又保证了线程安全,还解决了一些安全隐患!