本文介绍 常用的单例模式实现方式、使用场景、对各种实现优缺点探讨、笔者选用建议
简介
对象创建型模式
核心:一个类只生成唯一实例对象,构造方法私有化禁止外部创建对象。GoF对单例模式的定义是:保证一个类只有一个实例存在,同时提供能对该实例加以访问的全局访问方法(静态方法)
总结就是如下:
- 类中唯一实例引用
- 构造方法私有
- 一个静态方法访问该实例
为什么使用单例模式
- 共享资源
- 节省对象的创建时间等等
典型单例模式
- 饿汉式
- 懒汉式(饱汉)(多线程不能保证单例模式)
- 双重检查
- 静态内部类实现
多种实现方式
饿汉式
- 优点:多线程中能够保证单例
- 缺点:类一加载就实例化并且一直存在,浪费资源
- 推荐:可以使用
package com.mym.designmodel.singleton;
/**
* 饿汉单例模式
*/
public class ESingleton {
public static ESingleton eSingleton = new ESingleton();
private ESingleton(){}
public static ESingleton getSingleton(){
return eSingleton;
}
}
饱汉式
- 优点:能够懒加载
- 缺点:多线程中不能保证单例
- 推荐:不推荐使用
package com.mym.designmodel.singleton;
/**
* 饱汉模式
*/
public class BSingleton {
private static BSingleton bSingleton = null;
private BSingleton(){}
public static BSingleton getSingleton(){
if(bSingleton == null){
bSingleton = new BSingleton();
}
return bSingleton;
}
}
饱汉式线程安全
- 优点:能够懒加载,保证了多线程中的单例
- 缺点:阻塞模式,多线程执行效率较低
- 推荐:不推荐使用
package com.mym.designmodel.singleton;
/**
* 饱汉模式升级1
*/
public class BSingletonUp1 {
private static BSingletonUp1 bSingletonUp1 = null;
private BSingletonUp1(){}
//阻塞模式
public static synchronized BSingletonUp1 getSingleton(){
if(bSingletonUp1 == null){
bSingletonUp1 = new BSingletonUp1();
}
return bSingletonUp1;
}
}
双重检查模式单例实现
- 优点:能够懒加载、保证多线程中的单例、微阻塞
- 缺点:第一次访问仍然是阻塞的
- 推荐:推荐使用,效率可以
package com.mym.designmodel.singleton;
/**
* 双重检查模式
*/
public class DoubleCheckSingleton {
private static DoubleCheckSingleton doubleCheckSingleton = null;
private DoubleCheckSingleton(){}
//双重检查 阻塞模式
public static DoubleCheckSingleton getSingleton(){
if(doubleCheckSingleton == null){
synchronized (DoubleCheckSingleton.class){
if(doubleCheckSingleton == null){
doubleCheckSingleton = new DoubleCheckSingleton();
}
}
}
return doubleCheckSingleton;
}
}
静态内部类实现单例模式
- 优点:懒加载、多线程保证单例、非阻塞、效率高
- 缺点:较其他实现方式多了一个类资源(静态内部类也是一个类),不过影响不大
- 推荐:非常推荐,高效率
package com.mym.designmodel.singleton;
/**
* 饱汉模式:静态内部类实现,非阻塞,推荐使用
*/
public class Singleton {
private Singleton(){}
private static class InnerClass{
private final static Singleton instance = new Singleton();
}
public static Singleton getSingleton(){
return InnerClass.instance;
}
}
测试
本测试着力于是否保证单例。其他线程安全、执行效率,笔者就不贴代码了
package com.mym.designmodel.singleton;
/**
* 测试
*/
public class MainClass {
public static void main(String[] args) {
ESingleton eSingleton1 = ESingleton.getSingleton();
ESingleton eSingleton2 = ESingleton.getSingleton();
System.out.println("【ESingleton】是否为同一实例:" + (eSingleton1 == eSingleton2));
BSingleton bSingleton1 = BSingleton.getSingleton();
BSingleton bSingleton2 = BSingleton.getSingleton();
System.out.println("【BSingleton】是否为同一实例:" + (bSingleton1 == bSingleton2));
BSingletonUp1 bSingletonUp1 = BSingletonUp1.getSingleton();
BSingletonUp1 bSingletonUp2 = BSingletonUp1.getSingleton();
System.out.println("【BSingletonUp1】是否为同一实例:" + (bSingletonUp1 == bSingletonUp2));
DoubleCheckSingleton doubleCheckSingleton1 = DoubleCheckSingleton.getSingleton();
DoubleCheckSingleton doubleCheckSingleton2 = DoubleCheckSingleton.getSingleton();
System.out.println("【DoubleCheckSingleton】是否为同一实例:" + (doubleCheckSingleton1 == doubleCheckSingleton2));
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println("【Singleton】是否为同一实例:" + (singleton1 == singleton2));
}
}
输出结果:
【ESingleton】是否为同一实例:true
【BSingleton】是否为同一实例:true
【BSingletonUp1】是否为同一实例:true
【DoubleCheckSingleton】是否为同一实例:true
【Singleton】是否为同一实例:true
单例模式的扩展
单例目的是节约资源。难点在于线程安全和效率。而单例不止是如上的实现,在java体系中,最为模范的单例模式就是枚举类,而最常用到的单例模式也还有八个基本数据类型对应的封装类:Integer、Long、Float…查看源码即可知道,他们内部都是单例且不可修改的:
Integer a = new Integer(2);
Integer b = new Integer(2);
System.out.println(a == b);
结果为false。
源码:Integer的内部
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
由封装类可知,实际上使用final也可以实现伪单例模式。