单例模式及面试

单例模式的定义:

  :指一个类只有一个实例

单例模式的具体实现

  需要:

    (1)私有的构造方法,使其不能在类的外部通过new关键字实例化该类对象。

    (2)私有的静态实例化对象,并且将其封装为private static类型。

    (3)对外提供一个公共的访问方法并返回该实例对象

单例模式的两种实现:

第 1 种:饿汉式(立即加载):

    饿汉式就是 使用类的时候已经将对象创建完毕(不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故又被称为“饿汉模式”),常见的实现办法就是直接new实例化

public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static、final修饰
    private static final Singleton instance = new Singleton();
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例
    public static Singleton getInstance() {
        return instance;
    }
}

    

“饿汉式”的优缺点:

  优点:  

    实现起来简单,没有多线程同步问题

  缺点:

    当类Singleton被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。

第 2 种:懒汉式(延迟加载):

  该模式的特点   是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例

public class LazySingleton
{
private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
private LazySingleton(){} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance()
{
//getInstance 方法前加同步
if(instance==null)
{
instance=new LazySingleton();
}
return instance;
}
}

  优点:在多线程情形下,保证了“懒汉模式”的线程安全

  缺点:众所周知在多线程情形下,synchronized方法通常效率低,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

最佳的单例模式的实现:

    结合上面两种单例模式的实现 都存在缺陷  采用   DCL双检查锁机制(DCL:double checked locking) 

public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static Singleton instance;
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例
    public static Singleton getInstance() {
        // 第一次检查instance是否被实例化出来,如果没有进入if块
        if(instance == null) {
            synchronized (Singleton.class) {
                // 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

    上面是单例模式的最佳实现方式。内存占用率高,效率高,线程安全,多线程操作原子性

使用场景: 

    单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 

      1.需要频繁实例化然后销毁的对象。 
      2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
      3.有状态的工具类对象。 
      4.频繁访问数据库或文件的对象。

      5 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

原文地址:https://www.cnblogs.com/JonaLin/p/12718183.html