设计模式之单例模式

单例模式:

  对于某些应用场景下,某应用在整个系统运行期间,无需创建多份,典型应用如任务管理器,文档编辑工具(Office)。一是不会造成系统资源浪费,二不会出现内容不一致的情况。这样通过单例模式可以保证一致性。

  首先单例模式需要保证不能通过new关键字创建对象实例,及控制构造函数私有,然后通过统一的静态方法入口获取单一实例。

  单例模式常见的有两种:恶汉式、懒汉式。先看一下饿汉式模式

1、饿汉模式

  饿汉模式,即在类加载时就创建对象,代码如下

class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() { }
public static EagerSingleton getInstance() {

return instanct;}

2、懒汉模式

懒汉模式,即实现“”延迟加载”,对象实例声明为null ,当调用时进行new。代码如下

class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

但是懒汉有一个问题,即不能保证线程安全,考虑到多线程并发访问的时候可能导致对象不一致,为此可以为getInstance()方法前添加synchronized关键字,由于锁力度较大,可以考虑在instance = new LazySingleton();前增加synchronized代码块控制。另外这种情况也是会出现不一致的情况,通常类初始化较为复杂,可能两个线程都进入instance为null的判断,因此为保证一致性可以考虑在synchronized代码块中增加第二重为null判断,即双重锁检查(Double Lock Check)。

class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}

  注意:instance声明处,需要将其加上volatile关键字,可以确定多个线程可以正确处理。

3、饿汉与懒汉模式的优缺点

  饿汉式单例在类初始化加载时,即创建。无需考虑线程安全,可以确保实例的一致性。调用速度,反应时间要较懒汉式优秀。资源利用率上,类加载时都需要进行实例化对象,时间可能更长。

  懒汉式单例,第一次使用时占用系统资源,实现延迟加载,必须处理好多线程的访问安全性问题,可通过双重检查锁进行控制。

  有没有既能保证一致性,也可以提交访问效率、提高资源利用率的方式呢?

4、Initialization Demand Holder (IoDH)

  

//Initialization on Demand Holder
class Singleton {
private Singleton() {
}
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
public static void main(String args[]) {
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}

  

由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能。

(参考http://gof.quanke.name/)

原文地址:https://www.cnblogs.com/SmilingFish/p/11489000.html