单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式要求:这个类只能有一个实例,并且由它自行创建,创建过程中保证线程安全。
- 饿汉式-类被加载的时候立刻创建实例
public class SingletonThird { private static SingletonThird singleton = new SingletonThird(); private SingletonThird() { } public static SingletonThird getSingleton() { return singleton; } }
优点:完全无需考虑多线程的访问问题,自行可以保证线程安全,调用速度相对来说快一点;
缺点:无论系统是否需要这个实例,都会自动创建,会一直占用系统资源,从资源利用率来说相对较低。 - 懒汉式
单线程下懒汉式public class SingletonFirst { private static SingletonFirst singleton = null; private SingletonFirst() { } public static SingletonFirst getSingleton() { if (singleton == null) { singleton = new SingletonFirst(); } return singleton; } }
多线程同步锁单判断懒汉式public class SingletonSecond { private static SingletonSecond singleton = null; private SingletonSecond() { } public static synchronized SingletonSecond getSingleton() { if (singleton == null) { singleton = new SingletonSecond(); } return singleton; } public static SingletonSecond getSingleton2() { if (singleton == null) { synchronized (SingletonSecond.class) { singleton = new SingletonSecond(); } } return singleton; } }
使用同步锁的情况下,使用getSingleton方法获取对象,明显效率低,因为多线程同时执行该方法,那么会进行排队获取,高并发情况下会使得效率大大降低,因此不进行推荐;getSingleton2方法虽然只对new SingletonSecond()方法进行了额锁定,但是并不能保证线程安全,比如第一个线程通过了if (singleton == null)方法,拿到了锁进行执行,但是new方法执行时间较长,此时第二个线程也通过了if (singleton == null)方法,进入等待,这样就会产生两个SingletonSecond 对象,线程不安全。可以用下面的双重验证锁进行线程安全保证。
- 双重验证锁懒汉式
public class SingletonFourth { private volatile static SingletonFourth singleton = null; private SingletonFourth() { } public static SingletonFourth getSingleton() { if (singleton == null) { synchronized (SingletonFourth.class) { if (singleton == null) { singleton = new SingletonFourth(); } } } return singleton; } }
优点,由于大部分都是取对象操作,因此如果每次都进行加锁,效率太低双重锁的情况下,取对象不需要进入等待,提高效率。
需要注意的是,如果使用双重检查锁定来实现懒汉式单例类,需要在静态成员变量instance之前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理,且该代码只能在JDK 1.5及以上版本中才能正确执行。由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也不是一种完美的实现方式。 - IoDH(Initialization Demand Holder)-静态内部类方法
第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。public class SingletonFifth { private SingletonFifth() { } private static class Holder{ private static SingletonFifth singleton = new SingletonFifth(); } public static SingletonFifth getSingleton(){ return Holder.singleton; } }