Java面试基础篇——第十二篇:浅析单例模式

原创
2018/07/26 13:00
阅读数 81

单例模式(Singleton)的目的是为了保证一个类在系统中只有一个实例,并提供一个访问它的全局访问点,从而方便对实例个数的控制并节约系统资源而出现的解决方案。

单例模式的好处

  • 1.对于频繁使用的对象,可以减少new操作花费的时间,尤其是对于一些重量级的对象而言,会节约一笔非常可观的系统开销。
  • 2.由于new操作的减少,对系统内存的操作频率也会降低,这将减轻GC的压力,缩短GC停顿时间。

单例模式的使用场景

  • 1、有频繁实例化然后销毁的情况,也就是频繁的 new 对象,可以考虑单例模式;
  • 2、创建对象时耗时过多或者耗资源过多,但又经常用到的对象;
  • 3、频繁访问 IO 资源的对象,例如数据库连接池或访问本地文件;
  • 4,具体情况要根据项目来考虑。

单例模式的实现

单例模式可以有很多种,如饿汉式,懒汉式,双重检查锁形式,内部静态类形式,枚举形式等。本文介绍前四种,其他形式可自行扩展。阅读以下代码,希望大家对我在代码中提出的问题做一些思考。

/**
 * @author Lee
 * @// TODO 2018/7/26-10:24
 * @description 饿汉式
 */
public class HungrySingleton {

    // Q1: 构造函数为什么是private ?
    private HungrySingleton (){
        System.out.println("Singleton is created!");
    }

    // Q2: instance对象为什么是private static ?
    private static HungrySingleton instance = new HungrySingleton();

    public static HungrySingleton getInstance(){
        return instance;
    }
}

饿汉式 优点:这个单例的性能是非常好的,因为getInstance()方法只是简单地返回instance,并没有任何锁操作,因此它在并行程序中,会有良好的表现。缺点:Singleton实例在什么时候创建是不受控制的。对于静态成员instance,它会在类第一次初始化的时候被创建。这个时刻并不一定是getInstance()方法第一次被调用的时候。任何对Singleton方法或者字段的引用,都会导致类初始化,并创建instance实例,但是类初始化只有一次,因此instance实例永远只会被创建一次。

/**
 * @author Lee
 * @// TODO 2018/7/26-10:37
 * @description 懒汉式
 */
public class LazySingleton {
    private LazySingleton (){
        System.out.println("LazySingleton is create !");
    }

    private static LazySingleton instance = null;

    // Q3:为什么要用synchronized ?
    public static synchronized LazySingleton getInstance(){
        if(instance == null)
            instance = new LazySingleton();
        return instance;
    }
}

懒汉式 优点:精确控制instance的创建时间,它只会在instance被第一次使用时创建对象。这种实现的好处是,充分利用了延迟加载,只在真正需要时创建对象。缺点:并发环境下加锁,竞争激烈的场合对性能可能产生一定的影响。

/**
 * @author Lee
 * @// TODO 2018/7/26-10:46
 * @description 静态内部类实现
 */
public class StaticSingleton {
    private StaticSingleton(){
        System.out.println("StaticSingleton is create");
    }
    private static class SingletonHolder{
        private static final StaticSingleton instance = new StaticSingleton();
    }
    public static StaticSingleton getInstance(){
        return SingletonHolder.instance;
    }
}

静态内部类实现优点:

  • 1.首先getInstance()方法中没有锁,这使得在高并发环境下性能优越
  • 2.只有在getInstance()方法被第一次调用时,StaticSingleton的实例才会被创建(这种方法巧妙地使用了内部类和类的初始化方式)
  • 3.内部类SingletonHolder被申明为private,这使得我们不可能在外部访问并初始化它。而我们只可能在getInstance()内部对SingletonHolder类进行初始化,利用虚拟机的类初始化机制创建单例。
/**
 * @author Lee
 * @// TODO 2018/7/26-10:58
 * @description 双重检查锁
 */
public class DoubleCheckSingleton {
    private static volatile DoubleCheckSingleton instance = null;
    private DoubleCheckSingleton(){

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

问题解答

  • Q1: 把Singleton的构造函数设置为private。这点非常重要,这就警告所有的开发人员,不能随便创建这个类的实例,从而有效避免该类被错误的创建。
  • Q2: instance对象必须是private并且static的。如果不是private,那么instance的安全性无法得到保证。一个小小的意外就可能使得in-stance变成null。
  • Q3: 为了防止对象被多次创建,我们必须使用synchronized进行方法同步
展开阅读全文
打赏
0
7 收藏
分享
加载中
更多评论
打赏
0 评论
7 收藏
0
分享
返回顶部
顶部