常见的单例模式学习总结

1.单例模式介绍:

1)单例模式的作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

2)单例模式的优点:单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

3)单例模式有多种,评价一个单例模式的参数有,是否立刻加载还是延时加载,是否线程安全,调用效率是否高

立即加载和延时加载:立即加载就是使用类的时候已经将对象创建完毕,延时加载就是在get()方法时实例才被创建

线程安全:在多线程情况下,是不是保证创建的对象只有一个

调用效率是否高:在第一次创建完对象后,如果能够保证多个线程同时获取对象,调用效率就高,如果第一个线程获取对象的时候,另一个线程要等待,调用效率就不高。

2.常见的单例模式有五种:

常见:

  • 饿汉式(线程安全,调用效率高,不能延时加载)
  • 懒汉式(线程安全,调用效率不高,可以延时加载)

其他:

  • 双重检测锁式(线程安全,调用效率高,可以延时加载)可以理解为懒汉式的升级版
  • 静态内存类式(线程安全,调用效率高,可以延时加载)
  • 枚举单例

2.饿汉式(线程安全,调用效率高,不能延时加载):

扫描二维码关注公众号,回复: 1008825 查看本文章
public class Singleton{
	private static Singleton singleton = new Singleton();
	private Singleton(){
	}
	public static Singleton getInstance(){
		return singleton;
	}
}

对饿汉式的特点说明

立即加载:类加载的初始化阶段会去初始化类变量(静态变量),此时就开始创建对象而此时并未使用类,所以属于立即加载。

线程安全:初始化阶段也就是执行类构造器<clinit>()方法,因为虚拟机保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,所以是线程安全的(深入理解Java虚拟机226页)。

调用效率高:getInstance()没有同步,可以多个线程同时调用getInstance()方法获取同一个对象,所以调用效率高。

3.懒汉式(线程安全,调用效率不高,可以延时加载):

public class Singleton{
	private static Singleton singleton;
	private Singleton(){
	}
	public synchronized static Singleton getInstance(){
		if(singleton==null){
			singleton = new Singleton();
		}
		return singleton;
	}
}

对懒汉式的特点的说明:

支持延时加载:懒汉式在类加载的时候并没有通过构造方法初始化对象,而是在第一个调用getInstance()方法的时候才创建singleton对象,所以懒汉式支持延时加载(所谓延时加载的就是用的时候再加载)。

线程安全:懒汉式的线程安全问题,是在一个线程判断if(singleton==null)时开始创建Singleton对象但没有创建完成,此时引用singleton仍然为null,此时分配给这个线程的时间片执行完了,另一个线程获取了CPU的执行权,判断if(singleton==null)后开始创建对象,导致了对象被创建了两次,懒汉式中通过synchronized关键字修饰getInstance()方法,保证了在一个线程new创建一个对象的时候,保证了别的线程不会进入getInstace()方法,所以是线程安全的。

调用效率低:懒汉式的安全问题是在创建对象的时候出现的(创建了多个对象),但是如果对象已经创建完成,此时获取对象并不存在线程安全问题,使用synchronized修饰getInstance()方法,导致在一个线程获取对象时,另一个线程获取对象要等待,这导致了调用效率低。

4.双重检测锁式(i线程安全,支持延时加载,调用效率高)

public class Singleton
{
    private volatile static Singleton singleton = null;
    private Singleton()  {    }
    public static Singleton getInstance()   {
        if (singleton== null)  {
            synchronized (Singleton.class) {
                if (singleton== null)  {
                    singleton= new Singleton();
                }
            }
        }
        return singleton;
    }
}

1)懒汉式因为使用synchronized修饰方法,同一个时刻只有一个线程能获取创建完的对象,导致了效率低。双重检测锁式可以解决这个问题,它通过两个if(singleton==null)判断(双重检测的名称由来),在判断singleton不为空,说明singleton对象已经被创建后,直接返回该对象不需要同步,使得多个线程可以同时获取创建好的对象。在对象还没有被创建时,此时singleton==null,通过同步语句块,保证了if(singleton==null)和singleton=new Singleton()被一个线程执行完,而不会被别的线程打断。

2)volatile关键字的作用:volatile关键字的常见的两个作用是:可见性和防止指令重排序,在懒汉式中使用该关键字是为了防止执行重排序,实例化一个对象其实可以分为三步:(1)申请分配内存(2)调用构造方法初始化对象(3)将内存空间的地址赋值给对应的引用。由于操作系统可以进行指令重排序,导致实际的执行顺序也可能是(1)(3)(2)。在懒汉式中,singleton= new Singleton()不是原子操作,假设出现这样一种情况,线程一执行(1)(3)(2)的顺序并且只执行完了(1)(3),(2)还没有来得及执行,此时singleton指向了一个内存不为null,此时另一个线程获得CPU的执行权执行getInstance()方法,判断singleton!=null,直接返回singleton对象,而此时singleton还没有被创建,导致出错。

3)代码中volatile为什么没有起可见性的作用而是起到防止指令重排的作用

if (singleton== null)  {
            synchronized (Singleton.class) {
                if (singleton== null)  {
                    singleton= new Singleton();
                }
            }
也许第一个singleton的值没有保证可见性(读取的线程自己工作内存中的值),但是synchronized中的if(singleton==null)已经保证了singleton的可见性,所以这里的volatile还是起到防止指令重排的作用。

5.静态内部类式:

public class Singleton {
    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

疑问:好多代码都是定义的priavate static final Sigleton instance = new Singleton();???,标准写法是什么?上面的静态内部类方式参考的是Java对线程编程核心技术中的代码。

支持延时加载:SingletonHolder被定义为静态内部类,在Sinleton类加载的时候并不会创建singleton对象,只有getInstance()方法第一次被调用的时候,读取SingletonHolder的静态变量INSTSNCE时,立即对SinlgetonHolder类初始化(深入理解Java虚拟机210页——5中情况必须对类初试化)。

线程安全:因为初始化中的<clinit>函数是线程安全的所以,所以private static Singleton INSTANCE = new Singleton()这段代码是线程安全的。

调用效率高:获取对象的方法getInstance()并没有加同步语句,多个线程可以同时获取对象,所以效率高。

参考:

深入理解Java虚拟机

https://www.zhihu.com/question/56606703

http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization

https://blog.csdn.net/cselmu9/article/details/51366946

http://blog.51cto.com/devbean/203501

https://blog.csdn.net/haoel/article/details/4028232

https://blog.csdn.net/wm5920/article/details/12562145





猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80351020