在编程中,我们经常会遇到需要在整个应用程序中共享某些资源的情况,数据库连接、日志记录器、配置参数等,这些资源通常只能有一个实例,因为它们的创建和初始化成本很高,而且如果允许有多个实例,可能会导致数据不一致和其他问题,这就是单例模式的基本思想。
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点,这种模式通常用于那些需要频繁创建和销毁的对象,例如数据库连接池、线程池等。
下面我们来详细解析一下单例模式的实现方式和优缺点。
1、懒汉式(线程不安全)
public class Singleton { private static Singleton instance; private Singleton(){} public static synchronized Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }
懒汉式是最常见的单例模式实现方式,也是最简单的,它存在线程安全问题,当多个线程同时调用getInstance()方法时,可能会创建多个实例,为了解决这个问题,我们需要对getInstance()方法进行同步,但这样会降低性能。
2、饿汉式(线程安全)
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } }
饿汉式是在程序启动时就创建好单例对象,因此它是线程安全的,如果单例对象的创建过程非常耗时,那么在程序启动时就需要花费很长时间,由于单例对象在程序启动时就已经创建好,如果程序运行过程中不需要这个对象,那么就会浪费资源。
3、双重检查锁定(推荐)
public class Singleton { private volatile static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if (instance == null){ //第一次检查,避免不必要的同步开销 synchronized (Singleton.class){ //同步代码块确保只有一个线程可以执行到这行代码 if (instance == null){ //第二次检查,减少同步开销 instance = new Singleton(); //只有在第一个线程创建了实例后,其他线程才能看到它的存在,从而避免了重复创建实例的问题 } } } return instance; } }
双重检查锁定是Java推荐的单例模式实现方式,它既保证了线程安全,又减少了同步开销,在这个实现方式中,我们首先检查instance是否为null,如果为null,才进入同步代码块,在同步代码块中,我们再次检查instance是否为null,如果仍然为null,才创建新的实例,这样可以确保只有一个线程能够创建实例,从而避免了重复创建实例的问题。