设计模式[Java]:四种常见单例模式简介及其实现

设计模式[Java]:四种常见单例模式简介及其实现


基本概念

​ 单例模式属于设计模式中的创建型模式之一。

特点

​ 使用单例模式的对象只有一个实例。
​ 具体应用场景,比如QQ、Broswer这种单应用程序和插件、客户端,资源管理器等。

实现方法

实现方法目前主流的有:

  1. 饿汉式单例
  2. 懒汉式单例
  3. 双检锁单例
  4. 内部静态类单例

之所以分为这么多类型,主要围绕的是两个方面的问题,一是线程安全,二是内存占用。

不同类型的实现方式有利有弊,主要会围绕其实现难度,锁,懒加载,性能开销这四个方面评估,适合使用场景的就是最好的。

  • 饿汉(Eager Initialization)

    - 无锁
    - 类加载时就创建实例,不存在多线程同时访问的情况,避免了同步问题,但会产生内存浪费
    
    public class Singeton{
    	private static Singeton instance = new Singeton();
    	private Singeton (){}
    	public static Singeton getInstance(){
    		return instance;
    	}
    }
    
  • 懒汉(Synchronized Lazy Initialization)

    - 无锁,线程不安全
    - 懒加载,显式调用时才会进行实例加载,避免内存浪费
    public class Singeton{
    	private static Singeton instance;
    	private Singeton(){}
    	public static Singeton getInstance(){
    		if(instance == null) {
    			instance = new Singeton();
    			return instance;
    		}else {
    			return instance;
    		}
    	}
    }
    
    - 有锁, 线程安全
    - 懒加载
    - 效率低,高并发不适用
    public class Singeton{
    	private static Singeton instance;
    	private Singeton(){}
    	public static Singeton synchronized getInstance(){
    		if(instance == null) {
    			instance = new Singeton();
    			return instance;
    		}else {
    			return instance;
    		}
    	}
    }
    
  • 双检锁(Double-Checked Locking)

    - 实例化检查+锁检查,线程安全,在多线程环境下避免加锁的性能开销,性能较高
    - 具体性能高低取决于getInstance如何实现
    public class Singeton{
    	/**
    	* volatile关键字用于确保多线程环境下的可见性。
    	* volatile 是 Java 中的关键字,用于声明变量,确保多个线程能够正确地处理该变量。主要有两个作用:
    	* 可见性(Visibility): 当一个线程修改了被 volatile 修饰的变量的值,变化会对其它线程立即可见。背后的实现机制锁会直接控制内存,而非缓存。
    	* 禁止指令重排序(Preventing Instruction Reordering): volatile 修饰的变量的读写操作不能被重排序,确保了在多线程环境下的正确执行顺序。
    	*/
    	private static volatile Singeton instance;
    	private Singeton(){}
    	public static Singeton getInstance(){
    		// 第一次检查,若实例不存在则进入同步
    		if (instance == null) {
    			// 第二次检查,进入锁检查。实现线程安全,确保只有一个实例创建
    			synchronized (Singeton.class) {
    				if(instance == null) {
    					instance = new Singeton();
    				}
    			}
    		}
    	}
    }
    
  • 登记式 / 静态内部类(Static Inner Class)

    - 使用static inner class既实现了Lazy Init 又实现了Lazy Loading,与双检锁不同的是,双检锁会在类装载的同时进行初始化;
    	登记式将其分离,只有显式调用getIns时才会装载Holder并实例化ins,而不是Singeton加载时就完成了实例化。
    - 通过final定义常量实现单例
    - 通过内部类加载实现线程安全与延迟加载
    /**
    * 对于内部类的加载,Java虚拟机会保证在同一时刻只有一个线程对类进行初始化。通过类加载器的锁(Class Initialization Lock)实现。类加载器锁是对类进行初
    * 始化的唯一入口,使得多线程环境下只有一个线程能够操作类的初始化操作。类加载是由类加载器负责的,而类加载器在加载类时会采用一种单一的全局锁,锁是全局单 * 一的,所以不存在竞争问题。
    */
    
    public class Singeton{
    	private static class Holder{
    		private static final Singeton instance = new Singeton();
    	}
    	private Singeton(){}
    	public static final Singeton getInstance(){
    		return Holder.instance;
    	}
    }
    	
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值