一 什么是单例模式
单例模式是Java编程中最常用的设计模式之一,所谓单例就是在JVM(Java虚拟机)中时刻只有一个对象实例存在,那么它的好处也很明显,有如下几点:
- 某些类创建比较频繁,特别是大型的对象,如果使用单例模式,将会大大减少系统的开销;
- 减少New操作,降低系统内存的使用频率,减少GC压力;
- 避免系统混乱,一些类必须使用单例模式,例如控制交易流程的类等。
二 单例模式
1 饿汉式单例模式
public class Singleton {
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
/**
* 杜绝单例对象被反序列化时重新生成对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return singleton;
}
}
说明,在这种模式下,在Singleton类被加载时就对singleton对象进行实例化,不存在多线程同步的的问题,但在加载该类的过程会比较缓慢,也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。
2 懒汉式单例模式(线程不安全)
public class SingletonLazy {
private static SingletonLazy singleton = null ;
/**
* 构造方法私有化
* */
private SingletonLazy(){
}
public static SingletonLazy getInstance() {
if (null == singleton){
singleton = new SingletonLazy();
}
return singleton;
}
/**
* 杜绝单例对象被反序列化时重新生成对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return singleton;
}
}
说明,在这种模式下,在Singleton类被加载时没有对singleton进行实例化,调用getInstance方法,才对singleton进行实例化,但在处理多线程同步时,会出现问题,线程不安全。
3 方法名上加同步锁单例模式(线程安全)
public class SingletonLazySyn {
private static SingletonLazySyn singleton = null ;
/**
* 构造方法私有化
* */
private SingletonLazySyn(){
}
/**
* 使用关键字synchronized 锁方法
* @return
*/
public static synchronized SingletonLazySyn getInstance() {
if (null == singleton){
singleton = new SingletonLazySyn();
}
return singleton;
}
}
说明,在这种模式下,在一定程度上解决了多线程不安全的问题,但是在每次调用getInstance方法时,都会给方法上锁,而实际上只有第一次创建该类实例需要上锁,大部分是不需要的,这样一来就影响了应用的性能,不推荐使用这种方法。
4 双重检查单例模式
public class SingletonLazySyn2 {
private static SingletonLazySyn2 singleton = null ;
/**
* 构造方法私有化
* */
private SingletonLazySyn2(){
}
/**
* 使用关键字synchronized 锁对象
* @return
*/
public static SingletonLazySyn2 getInstance() {
if (null == singleton){
synchronized (SingletonLazySyn2.class){
if (null == singleton){
singleton = new SingletonLazySyn2();
}
}
}
return singleton;
}
/**
* 杜绝单例对象被反序列化时重新生成对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return singleton;
}
}
说明,在这种模式下,当singleton 为null 才创建该类实例,创建实例的时候会给该类加上同步锁,这样看似既解决了性能的问题、又解决了多线程不安全的问题,但是这也不是绝对的安全,由于在JVM中,Java指令对创建实例和赋值是分两步进行,其间JVM不能保证这两个操作执行的先后顺序,所以就可能会出现异常,例如两个线程threadA、threadB同时执行到if判断,然后会进行如下操作:
->如果threadA先到synchronized块,判断singleton为null,会执行singleton = new SingletonLazy2();
->然后threadB执行到synchronized块,但此时该类已经加上同步锁,该线程此时被阻塞不能往下执行;
->JVM将为新的对象分配内存,并赋值给singleton,注意此时如果JVM尚未初始化这个实例,线程threadA离开synchronized;
->此时threadB可以进入到synchronized块,判读判断singleton不为null,那么直接返回singleton(实际上singleton尚未初始化);
->threadB获取singleton后,拿来使用时,将会出现异常。
5 静态内部类单例模式
public class SingletonFac {
/**
* 构造方法私有化
* */
private SingletonFac(){
}
public static SingletonFac getInstance() {
return SingletonFactory.singleton;
}
public static class SingletonFactory {
private static SingletonFac singleton = new SingletonFac();
}
/**
* 杜绝单例对象被反序列化时重新生成对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
}
说明,在这种模式下,可以解决上述问题,在加载SingletonFac时不会对singleton进行实例化,在加载SingletonFactory类(调用getInstance方法)才会对其进行实例化,而且能够保证自始自终JVM只有一个singleton实例,也不存在多线程不安全的问题,所以这个是使用最多的。
6 登记式单例模式
public class SingletonMap {
/**设立静态变量,记录单例模式对象实例*/
private static Map<String, Object> map = new HashMap<String, Object>();
private SingletonMap (){
}
public static Object getInstance(String name) {
if (TextUtils.isEmpty(name)){
name = SingletonMap.class.getName();
}
if (map.containsKey(name) && null != map.get(name)){
return map.get(name);
}else {
try {
map.put(name,Class.forName(name).newInstance());
} catch (InstantiationException e) {
Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
} catch (IllegalAccessException e) {
Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
} catch (ClassNotFoundException e) {
Log.e(SingletonMap.class.getSimpleName() , e.getMessage());
}
return map.get(name);
}
}
}
说明,这个类用来管理、记录单例模式对象,当需要某个单例模式对象时,首先在map中去查找,如果存在直接取出返回;否则就通过class.forName(name).newInstance()创建实例,这样能够保证JVM始终只有一个name对应类的实例。
public class SingletonMapChild {
/**
* 使用关键字synchronized 锁方法
* @return
*/
public static synchronized SingletonMapChild getInstance() {
return (SingletonMapChild) SingletonMap.getInstance(SingletonMapChild.class.getName());
}
/**
* 杜绝单例对象被反序列化时重新生成对象
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException {
return getInstance();
}
}
说明,获取SingletonMapChild实例通过静态方法getInstance,其真正创建该类实例的过程是在SingleMap中的getInstance方法中。
参考:
https://blog.youkuaiyun.com/lanzhizhuxia/article/details/7922977
https://blog.youkuaiyun.com/itachi85/article/details/50510124