最近项目中为了系统中避免创建多个实例,teamleader让我把项目代码优化一下(应用单例模式),后面将单例模式的学习心得分享给团队同事,同事也给了我一些意见,现在写出来分享给大家,博友多多知道哈,写的不好给点意见哈!
现在从三方面讲解单例模式:
(1)单例模式概念
(2)单例模式特点
(3)常见的实现方式
单例模式概念:作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
单例模式特点:
常见的实现方式:从以下8中实现方式讲解
public classSingleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
上述代码很简单,但是是线程不安全的,多线程情况下不能正常工作,会创建多个实例。为什么呢?
private staticSingletoninstance;
private Singleton(){
}
public staticsynchronized Singleton getInstance() {
if (instance== null) {#1
instance = newSingleton();#2
}
return instance;#3
}
}
上述代码为线程安全的,为什么呢?
private staticSingleton instance = new Singleton();
private Singleton (){
}
public staticSingleton getInstance() {
return instance;
}
}
上述代码在类一加载便实例化instance,因此称为恶汉式(很饥渴的意思,哈哈)
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
private static class SingletonHolder {
private static final Singleton INSTANCE = newSingleton();
}
private Singleton (){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
相对于第三种方式,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。例如,如果实例化instance很消耗资源,我想让其延迟加载,另一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。
6) 双重校验锁: “双重检查加锁”的方式可以既实现线程安全,又能够使性能不受到很大的影响。
那么什么是”双重检查加锁“机制呢?
所谓双重检查加锁机制指的是:并不是每次进getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
重检查加锁机制的实现会使用一个关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。(对于volatile修饰的变量,系统内的线程所有的write都将先行发生于read)
说明:由于volatile关键字可能会屏蔽掉虚拟机中的一些必要的代码优化,所以运行效率并不是很高。因此一般建议,没有特别的需要,不要使用。也就是说,虽然可以使用“双重检查加锁”机制来实现线程安全的单例,但并不建议大量采用,可以根据情况来选用。
现列出实例代码讲解:
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getInstance() {
if (singleton == null) {#2
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
注:很显然上述代码是线程安全的,然而细心的朋友可以发现,如果把去掉第一重判断#2,此时的代码也是线程安全的,与单例第二种实现方式没有本质的区别,那么为什么加上#2呢,好处有:提高代码运行效率,当线程1实例了singleton后,此时线程2判断singleton不为空,那么synchronized同步代码并需要执行,因为加锁、解锁很大的影响系统性能,尽量少用。
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
private static final Singleton<AManager> sInstance = new Singleton<AManager>() {
@Override
protected AManager create() {
return new AManager();
}
};
public static AManager getInstance(){
return sInstance.get();
}
private AManager(){
System.out.println("AManager created!");
}
}