单例模式详述

什么是单例

一个类在整个系统中只有一个实例,而且这个实例是在类的内部通过一个private的构造方法构造的,外部不能调用其构造方法,只能获取它的实例。

为什么使用单例而不是静态方法

静态方法是基于对象,单例是面向对象的,面相对象的代码提供一个更好的编程思想。
如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,反之他就应该是非静态的。如果我们确实应该使用非静态的方法,但是在创建类时又确实只需要维护一份实例时,就需要用单例模式了。

单例的特点

1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。

单例的饿汉式

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,它是线程安全的。

1
2
3
4
5
6
7
8
9
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton{
private Singleton() {}
private static final Singleton single = new Singleton();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}

单例的懒汉式

懒汉式单例只有在使用时才会被实例化

1
2
3
4
5
6
7
8
9
10
11
12
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}

但是上述懒汉式单例是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造并保证了懒汉式单例的线程安全。
在getInstance方法上加同步

1
2
3
4
5
6
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}

如上,如果是在getInstance方法上加了一个synchronized,那么我每次去执行getInstace方法的时候都会受到同步锁的影响,这样运行的效率会降低,其实只需要在第一次创建Singleton实例的时候加上同步锁就可以了,那么便有如下的修改

1
2
3
4
5
6
7
8
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (single == null) {
single = new Singleton();
}
return single;
}
}

这样效果是和直接在方法上加synchronized完全一致的。所以要在synchronized的外面再加一层判断

1
2
3
4
5
6
7
8
9
10
public static Singleton getInstance() {
if (single == null) {
synchronized (Singleton.class) {
if (single == null) {
single = new Singleton();
}
}
}
return single;
}

只有在single还没被初始化的时候才会进入到第3行,然后加上同步锁。等single一但初始化完成了,就再也走不到第3行了,这样执行getInstance方法也不会再受到同步锁的影响,效率上会有一定的提升。这种方法叫做双重锁定(Double-Check Locking)。
Double Check Lock 实现单例,优点是既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用 getInstance() 不进行同步锁;缺点是第一次加载反应稍慢。

静态内部类式

第一次加载不会初始化 Singleton,只有在第一次调用 Singleton 的 getInstance() 才会初始化,第一次调用会导致虚拟机加载 SingletonHolder 类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
/**
* 单例模式(静态内部类的方式)
*/
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}

单例模式优缺点

优点:在内存中只有一个实例,减少了内存开支和性能开销;可以避免对同一资源文件的同时写操作;可以在系统设置全局的访问点,优化和共享资源访问。
缺点:一般没有接口,扩展困难;单例模式如果持有 Context,很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好是 Application Context。