您的位置:首页 > 其它

单例模式的常见高效实践

2016-03-14 04:27 302 查看
单例类一般来讲是仅仅只实例化一次的类,它用来代表系统中唯一的组件,当然单例也避免了大量对象的创建,在工程实践中也有很多应用,比如java中的每个类的类对象class、spring框架中广泛使用单例组件、安卓中的EventBus.getDefault()、Volley等等,虽然有时候他们提供了可再次实例化的途径,但一般情况下,除非这个目前唯一实例已经不能满足系统需要,否则我们就要维护这个实例的单例性。

**

因为现在的系统很少是在单线程下运行,所以这里先只说几种常见的适合大多数环境下的单例模式

**

1、直接在类加载时就实例化的饿汉模式

具体什么时候类会加载请读者自己去度娘。这种方式是在变量第一次声明的时候初始化,清晰明了,可读性强,也没有并发环境下的排队访问等性能问题。也有人说会有一些资源消耗问题或者启动时的性能问题,我想问你确定有吗?对象延迟初始化会把启动时间提高1ms吗?除非你有足够的证据证明这么做有性能问题,否则不建议你去把代码编码复杂。一般情况下这个case可以优先使用,当然重构时你也可以考虑第二三种方案

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


2、私有静态内部类持有实例的懒汉模式

因为一般情况下JVM对每个类一般只会加载一次,(除非你使用多个类装载器来加载类)INSTANCE 对象是在类SingletonHolder类加载的时候实例化,也就是只有当调用getInstance的时候对象才实例化一次。。。这种方式相比上一种方式也是非常高效的,并发环境时也不需要排队,内存只在需要的时候才会分配。这种方式本人也是比较推荐的。。。

class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}


3、最推荐使用的枚举方式——-清晰、语义最明确、代码最简洁

枚举的特性天生就保证了对象的唯一性,也不存在子例化可能性,而且代码清晰明了,非常容易实现,是我目前为止碰到最为高效的一种单例实现方法。

enum Singleton {
INSTANCE;
}


4当然单例模式还有其他很多变种,比如spring中的工厂模式创建类对象,也是比较优秀的单例实现方式,这里也劝大家如果一个对象有工厂方法来创建它的话,请优先采用工厂方法,因为厂商提供这种途径来创建对象,一般基本能满足我们的需求,而且工厂方法中我们可以做不少逻辑,比如单例、缓存、子例化而且还可以有一个清晰的名字等等,如Integer.valueOf()提供了缓存功能、Executors.newCachedThreadPool()、Executors.newFixedThreadPool(),给实例一个清晰明了的名字,Arrays.asList()创建了一个子类化的实例等等。

———–还有其他几种单例模式的实现

1、单线程环境下的单例模式,并发环境下请慎用这种方式,如果多个线程同时执行到 if(instance==null)会怎么样?这个单例类会只有一个独一无二的实例对象吗?

class Singleton {
private static Singleton instance = null;

private Singleton() {
}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}


2、采用同步来精确实现单例对象的懒加载,这种方式和上面相比虽然确保了单例的只有一个实例,但是大量并发访问时,很多线程会在这里排队。实际上只有一个读操作,没那么必要弄个排队访问了,而且可读性也不如上面几种方案。

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


3、双重锁检查方式实现的单例模式,这种情况虽然比上面情况要好了很多,同样可以采取延迟初始化,还可以基本避免并发访问时的一个排队问题。。虽然你采用了一个很巧妙的double check,但是有必要吗,简单的一个单例模式,非要弄的这么复杂吗,我们写程序的一个目标就是尽量简洁明了啊,明明有更好的解决方案吗。。。而且这里还有一个隐含的bug导致你的程序失败,在JDK1.5以前版本,由于虚拟机的优化,虽然串行程序执行的结果是正确的,但是程序在计算机内部并不一定是按照我们设想的指令顺序运行,这一点其他线程是不知晓的。。。虽然这种可能性发生的概率非常小,但是谁又能保证不会发生呢,夜路走多了总会碰到鬼的,我们写程序的时候一定不要抱着侥幸的心态,程序的安全性和稳定性也是我们应该追求的啊。只要有可能改善我们现有的代码,就要不遗余力的去改进他,不要老是想着等以后我有时间了我会考虑的,而现实是那个时候你不会想起这个来,那个时候还会有其他一堆的事在等你。

class SingleTon {
private static SingleTon instance = null;

private Singleton () {
}

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


欢迎大家来交流,或赞或喷皆可,说的好的我会感激,说的不好的我也不会介意。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: