单例模式

概念:

单例模式(Singleton),也叫单子模式,是一种常用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候,整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,显然,这种方式简化了在复杂环境下的配置管理。

实现方式:

1.是在调用方法的时候进行实例化的工作,这种方式俗称懒汉模式;

2.是在类的加载的时候就创建一个实例,这种方式俗称饿汉模式;

实现单例模式:

1、懒汉式,线程不安全:

public class Boss {
    private Boss() {
    }
    private static Boss boss = null;
    public static Boss getBoss(){
        if(boss == null) {
            boss = new Boss();
        }
        return boss;
    }
}

 

public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            pool.execute(new Thread(){
                @Override
                public void run() {
                    Boss boss = Boss.getBoss();
                    System.out.println(boss);
                }
            });
        }
    }

运行结果:

描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

2、懒汉式,线程安全

public class Boss {
    private Boss() {
    }
    private static Boss boss = null;
    public static synchronized Boss getBoss(){
        if(boss == null) {
            boss = new Boss();
        }
        return boss;
    }
}

运行结果:

 

描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

3、双重校验锁

public class Boss {
    private Boss() {
    }
    private static Boss boss = null;
    public static synchronized Boss getBoss(){
        if(boss == null) {
            synchronized (Boss.class){
                if(boss == null) {
                    boss = new Boss();
                }
            }
        }
        return boss;
    }
}

运行结果:

描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

优点:安全且保持高效性。

4、静态内部类

public class Boss {
    private Boss() {
    }

    static class Lazy{
        private static Boss instance = new Boss();
    }

    public static Boss getInstance() {
        return Lazy.instance;
    }

}

运行结果:

描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

三要素: 

私有的构造方法;
指向自己实例的私有静态引用;
以自己实例为返回值的静态的公有方法。

优点:

内存中只有一个对象,节省内存空间;
避免频繁的创建销毁对象,可以提高性能;
避免对共享资源的多重占用,简化访问;
为整个系统提供一个全局访问点。

 

posted @ 2018-08-14 11:38  xiaobai1007  阅读(130)  评论(0编辑  收藏  举报