详解Java实现单例模式(面试题)懒汉式饿汉式

1.设计模式的说明

1.1 理解

设计模式是在大量的实践中总结和理论化之后优的代码结构、编程风格、以及解决问题的思考方式。

1.2 常用设计模式 — 23种经典的设计模式 GOF
创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

2.单例模式

2.1 要解决的问题:
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。

2.2 具体代码的实现:

饿汉式1class Bank{
	
	//1.私化类的构造器
	private Bank(){
		
	}
	
	//2.内部创建类的对象
	//4.要求此对象也必须声明为静态的,随着类的加载而创建
	private static Bank instance = new Bank();
	
	//3.提供公共的静态的方法,返回类的对象
	public static Bank getInstance(){
		return instance;
	}
}

饿汉式2:使用了静态代码块
class Order{
	
	//1.私化类的构造器
	private Order(){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Order instance = null;

	static{
		instance = new Order();
 }
	
	//3.声明public、static的返回当前类对象的方法
	public static Order getInstance(){
		return instance;
	}
	
}

懒汉式:
class Order{
	
	//1.私化类的构造器
	private Order(){
		
	}
	
	//2.声明当前类对象,没初始化
	//4.此对象也必须声明为static的
	private static Order instance = null;
	
	//3.声明public、static的返回当前类对象的方法
	public static Order getInstance(){
		
		if(instance == null){//懒汉式暂时还存在线程安全问题, 多线程情况下,
				多个线程同时执行到 if(instance==null)语句,创建多个引用对象。
			
			instance = new Order();
			
		}
		return instance;
	}
	
}

2.3 两种方式的对比:

  • 饿汉式:

  • 坏处:

    • 饿汉式是一种常见的单例模式实现方式,它的主要特点是在类加载时就创建并初始化单例对象,保证了在多线程环境下的线程安全性。虽然饿汉式有一些优点,但也存在一些潜在的坏处:
    • 提前占用内存空间:饿汉式在类加载时就创建单例对象,无论后续是否会真正使用该对象,都会占用一定的内存空间。如果单例对象较大或初始化耗时较长,可能会导致程序启动变慢,浪费内存资源。
    • 无法延迟加载:饿汉式无法实现延迟加载,即在需要使用单例对象时再进行创建。因为在类加载时就创建了单例对象,无论后续是否真正需要使用,对象都已经存在。
    • 可能造成资源浪费:如果在程序运行过程中始终没有使用该单例对象,而它又是在类加载时就创建的,就会造成资源的浪费。特别是在单例对象比较大或初始化代价较高的情况下,这种资源浪费可能会显著影响性能和资源利用率。
    • 无法处理异常情况:在饿汉式中,如果在单例对象的初始化过程中发生异常,可能会导致整个应用程序启动失败。这是因为单例对象的创建发生在类加载阶段,而异常无法捕获和处理。
  • 好处:饿汉式是线程安全的

  • 懒汉式:好处:延迟对象的创建。

  • 目前的写法坏处:线程不安全。

  • 懒汉式单例模式在多线程环境下存在线程安全问题,主要原因是多个线程可以同时进入到 if (instance == null) 的判断条件中,从而导致创建多个实例对象。

考虑以下情况:

  • 当线程 A 和线程 B 同时执行到 if (instance == null) 时,它们都发现 instance 为 null,满足条件。

  • 线程 A 进入 if 语句块,开始创建 Order 对象,并执行 instance = new Order();。

  • 在线程 A 执行完毕之前,线程 B 也进入 if 语句块,开始创建另一个 Order 对象,并执行 instance = new Order();。

  • 最终,就会创建两个不同的 Order 对象,违背了单例模式的原则。

  • 这种情况下,多线程并发访问会导致多个实例对象的创建,破坏了单例模式的唯一性。

使用同步机制将单例模式中的懒汉式改写为线程安全的。



class Bank{

    private Bank(){}

    private static Bank instance = null;

    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if(instance == null){
//
//                instance = new Bank();
//            }
//            return instance;
//        }
        //方式二:效率更高
        if(instance == null){ //后面来的线程 如果已经创建好了对象 就可以不进入同步代码块 直接return给一个单例对象。

            synchronized (Bank.class) {
                if(instance == null){

                    instance = new Bank();
                }

            }
        }
        return instance;
    }getInstance() 方法上添加 synchronized 关键字,将该方法变为同步方法
,确保在同一时间只有一个线程可以进入该方法,从而避免并发创建多个实例对象。
但这会影响性能,因为每次调用 getInstance() 都需要进行同步。

使用双重检查锁定(double-checked locking)机制,在 getInstance() 方法内部进行双重判断和同步处理。
在第一次检查时,判断 instance 是否为 null,如果是才进入同步块进行实例对象的创建。
这样可以避免每次调用 getInstance() 都进行同步,提高性能。
}

面试题:写一个线程安全的单例模式。
饿汉式。
懒汉式:上面提供的。

你可能感兴趣的:(Java面向对象,java,单例模式,开发语言)