java:单例模式的五种实现方式[通俗易懂]

java:单例模式的五种实现方式[通俗易懂]基于java实现到单例模式

大家好,又见面了,我是你们的朋友全栈君。

1. 概述

java单例模式是一种常见的设计模式。

单例模式有以下特点:

  1. 单例类只能有一个实例;
  2. 单例类必须自己创建自己的唯一实例;
  3. 单例类必须给所有其他对象提供这一实例;

2. 优缺点

  • 优点:由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统运行效率。
  • 缺点:因为系统中只有一个实例,导致了单例类的职责过重,违背了“单一职责原则”,同时不利于扩展。

3. 单例模式实现方式

常见的单例模式实现方式有五种:饿汉式懒汉式双重检测锁静态内部类枚举单例

3.1 饿汉式

public class SingletonDemoInHunger { 
   
	
	// 私有实例,类初始化就加载
	private static SingletonDemoInHunger instance = new SingletonDemoInHunger();
	
	// 私有构造方法
	private SingletonDemoInHunger() { 
   }
	
	// 公共的、静态的获取实例方法
	public static SingletonDemoInHunger getInstance() { 
   
		return instance;
	}
}

饿汉式

  • 类加载时就初始化,浪费内存,不能延迟加载
  • 基于 classloader 机制避免了多线程的同步问题,线程安全
  • 没有加锁,调用效率高

3.2 懒汉式

public class SingletonDemoInLazy { 
   
	
	// 私有实例,初始化的时候不加载(延迟加载)
	private static SingletonDemoInLazy instance;
	
	// 私有构造
	private SingletonDemoInLazy() { 
   }
	
	// 公共获取实例方法(线程不安全)
	public static SingletonDemoInLazy getInstance() { 
   
		if(instance == null ) { 
    // 使用的时候加载
			instance = new SingletonDemoInLazy();
		}
		return instance;
	}
}

上面这种写法,是线程不安全的,但是可以做到延迟加载

下面是线程安全的懒汉模式:

public class SingletonDemoInLazy { 
   
	
	// 私有实例,初始化的时候不加载(延迟加载)
	private static SingletonDemoInLazy instance;
	
	// 私有构造
	private SingletonDemoInLazy() { 
   }
	
	// 公共获取实例方法(线程安全,调用效率低)
	public synchronized static SingletonDemoInLazy getInstance() { 
   
		if(instance == null ) { 
   
			instance = new SingletonDemoInLazy();
		}
		return instance;
	}
}

上面代码中,通过关键字synchronized声明公共的获取实例的方法getInstance(),可以确保线程安全,能做到延迟加载,但是效率不高

3.3 double-checked locking(双重检查锁)

public class SingletonDemoInDoubleCheckLock { 
   
	
	// 私有实例,volatile关键字,禁止指令重排。
	private volatile static SingletonDemoInDoubleCheckLock instance;
	
	// 私有构造
	private SingletonDemoInDoubleCheckLock() { 
   }
		
	// 公共获取实例方法(线程安全)
	public static SingletonDemoInDoubleCheckLock getInstance() { 
   
		if(instance == null ) { 
    // 一重检查
			synchronized (SingletonDemoInDoubleCheckLock.class) { 
   
				if(instance == null) { 
    // 二重检查
					instance = new SingletonDemoInDoubleCheckLock();
				}
			}
		}
		return instance;
	}
}

在加锁之前判断是否为空,可以确保 instance 不为空的情况下,不用加锁,可以直接返回。

加锁之后,还需要判断 instance 是否为空,是为了防止在多线程并发的情况下,会实例化多个对象。例如:线程 a 和线程 b 同时调用 getInstance 方法,假如同时判断 instance 都为空,这时会同时进行抢锁。假如线程 a 先抢到锁,开始执行 synchronized 关键字包含的代码,此时线程 b 处于等待状态。线程 a 创建完新实例了,释放锁了,此时线程 b 拿到锁,进入 synchronized 关键字包含的代码,如果没有再判断一次 instance 是否为空,则可能会重复创建实例。

双重检查锁

  • 双重判断,延迟加载;
  • 线程安全;
  • JDK 版本要求1.5起。

3.4 静态内部类

public class SingletonDemoInStaticInnerClass { 
   
	
	// 静态内部类
	private static class InnerClass{ 
   
		// 初始化实例
		private final static SingletonDemoInStaticInnerClass INSTANCE = new SingletonDemoInStaticInnerClass();
	}
	
	// 私有构造
	private SingletonDemoInStaticInnerClass() { 
   }
	
	// 公关获取实例方法(线程安全,延迟加载)
	public static SingletonDemoInStaticInnerClass getInstance() { 
   
		return InnerClass.INSTANCE;
	}
}

静态内部类

  • 利用了classloader机制来保证初始化 instance 时只有一个线程,线程安全
  • 只有通过显式调用 getInstance 方法时,才会显式装载静态内部类,从而实例化instance延迟加载

3.5 枚举

public enum SingletonEnum { 
   
	
	// 枚举元素本身就是单例 
	INSTANCE;
	
	
	// 其他要执行的方法
	public void sayHello() { 
   
		System.out.println("你好");
	}
	......
}

枚举:这是实现单例模式的最佳方法。它更简洁,不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。但是不是延迟加载的。

如何选用哪种方式实现单例模式?
一般情况下,不建议懒汉式,建议使用饿汉式;只有在要明确实现延迟加载效果时,才会使用静态内部类;如果涉及到反序列化创建对象时,可以尝试使用枚举;如果有其他特殊的需求,可以考虑使用双重检查锁。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/156328.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • <noscript>元素[通俗易懂]

    <noscript>元素[通俗易懂]早期浏览器都面临一个特殊的问题,即当浏览器不支持JavaScript时如何让页面平稳地退化。对这 个问题的最终解决方案就是创造一个<noscript>元素,用以在不支持JavaScript的浏览器中显示替代 的内容。这个元素可以包含能够出现在文档<body>中的任何HTML元素——<script>元素除外。包含 在<noscript&gt…

    2025年6月10日
    0
  • mapminmax 用法「建议收藏」

    mapminmax 用法「建议收藏」mapminmax是MATLAB实现归一化的工具包,默认:(1)将矩阵的每行分别进行归一化;(2)每行的最大值最小值作为每行归一化的xmin和xmax;(3)将数据归一化到[-1,1].若要将数据归一化到0到1之间,即y∈[0,1],使用b=mapminmax(a,0,1);若给与确定的最大值和最小值作为每行的xmin和xmax,使用:b= mapminmax(a,0,1);PS.xmin…

    2022年6月30日
    32
  • tableau旭日图_Echart

    tableau旭日图_Echart效果图源代码ECharts//基于准备好的dom,初始化echarts实例varmyChart=echarts.init(document.getElementById(‘main’));varoption;option={silent:true,series:{radius:[‘15%’,’80%’],type:’sunburst’,sort:null,highligh…

    2022年9月25日
    0
  • Java中containsKey()方法[通俗易懂]

    Java中containsKey()方法[通俗易懂]containsKey方法——判断是否包含指定的键名在HashMap中经常用到containsKey()来判断键(key)是否存在。HashMap中允许值对象(value)为null,并且没有个数限制,所以当get()方法的返回值为null时,可能有两种情况:一种是在HashMap中没有该键对象,另一种是该键对象没有映射任何值对象,即值对象为null。因此,在HashMap中不应该利用get()方法来判断是否存在某个键,而应该利用containsKey()方法来判断。例如:HashMap&l

    2022年6月22日
    41
  • vagrant up提示“Couldn‘t open file /path/to/base“的错误解决方法

    vagrant up提示“Couldn‘t open file /path/to/base“的错误解决方法

    2021年10月28日
    54
  • http://www.objectarx.net/forum.php?mod=forumdisplay&fid=18&page=2

    http://www.objectarx.net/forum.php?mod=forumdisplay&fid=18&page=2http://www.objectarx.net/forum.php?mod=forumdisplay&fid=18&page=2

    2022年7月4日
    17

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号