单例常见实现方式

  • 饿汉式:在类被加载时就初始化单例
  • 懒汉式:在需要的地方才初始化单例

饿汉式加载问题

传统的在类加载时就创建单例的方法存在一些问题:

1
2
3
4
5
6
7
8
9
10
class SingleInstance {

private SingleInstance() {...}

private static SingleInstance mInstance = new SingleInstance();

public static SingleInstance getInstance(){
return mInstance;
}
}

如果类的实例化含义太多的操作,就可能会影响程序的性能,启动时间等。而且如果单例在程序中并未用到,则会一直存在一个无法被GC回收的对象,造成浪费。

如果单例一定会使用,而且构造方法中又没有太多操作,则可以在类加载时就初始化单例。

懒汉式加载问题

下面这种懒加载,在多线程情况下可能会造成创建了多个单例。
线程A正在执行new操作,线程B同时来获取单例,此时instance还是null,线程B就也会去执行new操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
class SingleInstance {

private SingleInstance() {}

private static SingleInstance mInstance;

public static SingleInstance getmInstance() {
if (null == mInstance) {
mInstance = new SingleInstance();
}
return mInstance;
}
}

使用synchronized优化懒加载

  • 方式一:对获取方法加锁(不推荐)
1
2
3
4
5
6
public synchronized static SingleInstance getInstance(){
if(null==mInstance){
mInstance = new SingleInstance();
}
return mInstance;
}

这种方式加锁,会造成性能问题。synchronized修饰了方法,所有线程获取单例时都会经历同步锁,不论单例是否已经被创建。而且获取单例可能是个频繁的操作,直接对方法加锁会降低性能。

  • 方式二:双重验证,只对构建加锁
1
2
3
4
5
6
7
8
9
10
private volatile static SingleInstance mInstance;

public static SingleInstance getInstance() {
if (null == mInstance) {
synchronized (SingleInstance.class) {
mInstance = new SingleInstance();
}
}
return mInstance;
}

推荐这种方式实现单例懒加载,只当单例没有被构造时才会进入加锁的部分。而且使用volatitle关键字,让所有线程都能拿到最新的单例。

使用静态内部类优化懒加载

静态内部类只有在被调用时才会被加载,而且静态方法只会执行一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
class SingleInstance {

private SingleInstance() {}

public static SingleInstance getInstance() {
return SingleInstanceHolder.mInstance;
}

public static class SingleInstanceHolder {
private static SingleInstance mInstance = new SingleInstance();
}

}


Java      单例

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!