宁波Java培训
达内宁波中心

13429669395

热门课程

达内:java单例模式知识总结

  • 时间:2016-01-19
  • 发布:宁波java培训
  • 来源:慕课社区


    宁波达内java培训专家详细讲解java单例模式的用法。

    当某一些类应该只存在一个实例的时候,我们就可以用单例模式。

    单例模式:确保一个类只有一个实例,并提供一个全局访问点。单例模式是所有设计模式中最简单的一个,也是大部分人最早知道的一个设计模式.。

    经典的单例
 
    常见的单例模式代码:

public class Singleton {

    private static Singleton ins;
    private Singleton() {}
    public static Singleton getIns() {
        if (null == ins) {
            ins = new Singleton();
        }
        return ins;
    }
}

    简单总结一下这样的写法:

1.提供一个全局静态的getIns方法,使得易于使用
2.延迟Singleton的实例化,节省资源(所谓的懒汉式)
3.缺点是线程不安全。当多个线程同时进入if (null == ins) {}的时候就会创建多个实例

    多线程安全
 
    加一个synchronized能不能行呢?

public static synchronized Singleton getIns() {
    if (null == ins) {
        ins = new Singleton();
    }
    return ins;
}

    增加synchronized之后能够迫使每个线程在进入这个方法之前,要先等别的线程离开该方法。也即避免了多个线程同时进入getIns方法

    诚然这个能解决问题,但是我们知道synchronized是非常耗性能的。

    更何况我们只需要在第一次执行这个方法的时候同步,也就是说当ins实例化后,我们不再需要同步了。

    而如果我们加了synchronized,那么实例化后的每次调用getIns都是一种多余的消耗操作,完全没必要。

    那么如何改善?如何确保单例,而又不影响性能?

    性能进阶
 
    接下去介绍一种更优秀的,线程安全的单例写法---双重检查锁模式(double check locking pattern)

public class DoubleCheck {

    private volatile static DoubleCheck ins;
    private DoubleCheck() {}
    public static DoubleCheck getIns() {
        if (null==ins){ //检查
            synchronized (DoubleCheck.class){
                if (null == ins) { //又检查一次
                    ins = new DoubleCheck();
                }
            }
        }
        return ins;
    }
}

    注意这里的ins用了volatile关键字来修饰,为什么呢?

    因为执行ins = new DoubleCheck()做了很多事情:

1.给ins分配内存
2.调用构造函数来初始化成员变量(可能会很久)
3.将ins对象指向分配的内存空间(执行完这步 ins才不为null)

    上面的操作并不是原子操作,而jvm也可能重排序指令,导致第二三两步的执行顺序可能会被打乱,当第3步先于第2步完成,那么会导致有线程拿到了初始化未完毕的ins,那么就会错误,而这里利用了volatile的禁止指令重排序优化特性,用来解决这个问题。

    注:volatile 在java 5 后才有效。

    达内java培训专家指出:双重检查非常适用于高并发,我们熟知的开源库Eventbus,ImageLoader等都是用的双重检查锁方式实现单例。

    饿汉式
 
java代码:

public class Early {
    private static final Early ins = new Early();
    public static Early getIns() {
        return ins;
    }
}

    饿汉式的原理其实是基于classloder机制来避免了多线程的同步问题

    饿汉式与之前提到的懒汉式不同,它在我们调用getIns之前就实例化了,所以不是一个懒加载,这样就有几个缺点:

1.延长了类加载时间
2.如果没用到这个类,就浪费了资源(加载了但是没用它)
3.不能传递参数(很显然适用的场景会减少)

    静态内部类
 
    静态内部类原理同上,另外虽然它看上去有点饿汉式,但是与之前的饿汉有点不同,它在类Singleton加载完毕后并没有实例化,而是当调用getIns去加载Holder的时候才会实例化,静态内部类的方式把实例化延迟到了内部类的加载中去了!所以它比饿汉式更优秀!

例子:

public class Singleton {

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

    枚举
 
    最后达内java培训专家介绍一种方法,也是从 EffectiveJava中看到的。

public enum Singleton{
    INSTANCE;
}

    优点:简单,线程安全,防反序列化.

    总结

    单例的实现方式很多,实际开发中看需求而定,达内java培训专家推荐使用双重检查锁和静态内部类这两种方法。








原文作者:程序亦非猿
原文链接:http://www.imooc.com/article/4038
上一篇:达内与阿里云达成战略合作,培养大数据技术人才
下一篇:达内:java面向对象的相关知识

达内java大数据班就业喜报,最高月薪达18000元

795万高校毕业生创历史新高,2017届毕业生就业近况几何?

达内Linux学员毕业2周就业率96%,最高薪资10000元

达内教育总裁韩少云受邀出席GIE国际教育峰会做主题演讲

选择城市和中心
贵州省

广西省

海南省