六种单例模式

Posted NewWorldForU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了六种单例模式相关的知识,希望对你有一定的参考价值。

目录

1、 适合小对象的单例

/**
 1. 如何保证如下类的实例对象在内存中只有一份
 2. (1)让外界从一个池中取对象(通过池保证类的实例对象只有一个)
 3. (2)让外界直接通过类的静态方法获取类内部创建的一个实例
 */
class Singlenton01//应用场景:适合小对象
	static Singlenton01 instance = new Singlenton01();
	private Singlenton01() 
	public static Singlenton01 getInstance() 
		return instance;
	

饿汉式的优点:方便,简单,易于理解。
饿汉式的缺点:如果对象是大对象,在创建了大对象之后很久不去使用则会造成内存的极大的浪费。

2、 适合大对象,多线程,性能要求不高,第一版懒加载

/**
	 * 线程不安全?导致线程不安全的原因是什么?
	 * (1)多个线程并发执行
	 * (2)多个线程有共享数据集(例如instance)
	 * (3)多个线程在共享数据上的操作不是原子操作(所谓原子操作是指不会被线程调度机制打断的操作)
	 */
class Singlenton02//应用场景:适合大对象,单线程	
	private static Singlenton02 instance;
	private Singlenton02() 
		System.out.println("Singlenton02.Singlenton02()");
	
	public static Singlenton02 getInstance() 
		if(instance == null) 
			instance = new Singlenton02();
		
		return instance;
	

3、 适合大对象,多线程,性能要求不高,第二版懒加载,单重判断

class Singlenton03//应用场景:适合大对象,多线程,性能要求不高
	private static Singlenton03 instance;
	private Singlenton03() 
		System.out.println("Singlenton03.Singlenton03()");
	
	public static synchronized Singlenton03 getInstance() 
		if(instance == null) 
			instance = new Singlenton03();
		
		return instance;
	

4、 适合大对象,多线程,性能要求不高,第三版懒加载,双重判断

class Singlenton04//应用场景:适合大对象,多线程,性能要求不高
	/**
	 *优化: 在保证线程安全的情况下,尽量保证线程阻塞
	 */
	
	private static volatile Singlenton04 instance;
	/**
	 * volatile关键字的作用
	 * (1)禁止指令重排序(jvm对JAVA程序进行编译时会有指令重排序)
	 * (2)保证可见性(多CPU,一个线程对某个线程共享变量的修改对另一个线程可见的)
	 * (3)但不保证原子性
	 */
	private Singlenton04() 
		System.out.println("Singlenton04.Singlenton04()");
	
	public static Singlenton04 getInstance() 
		if(instance == null) 		//1位置
			synchronized (Singlenton04.class) 
				if(instance == null) 
					instance = new Singlenton04(); 		//2位置
				
				
			
		
		return instance;
	

这种方式使用双重检查锁,多线程环境下执行getInstance()时先判断单例对象是否已经初始化,如果已经初始化,就直接返回单例对象,如果未初始化,就在同步代码块中先进行初始化,然后返回,效率很高。

但是这种方式有一个重要的优化,问题的根源出在位置2

sInstance =new Singleton();这句话创建了一个对象,他可以分解成为如下3行代码:

memory = allocate();  // 1.分配对象的内存空间
ctorInstance(memory);  // 2.初始化对象
sInstance = memory;  // 3.设置sInstance指向刚分配的内存地址

上述伪代码中的2和3之间可能会发生重排序,重排序后的执行顺序如下

memory = allocate();  // 1.分配对象的内存空间
sInstance = memory;  // 2.设置sInstance指向刚分配的内存地址,此时对象还没有被初始化
ctorInstance(memory);  // 3.初始化对象

因为这种重排序并不影响Java规范中的规范:intra-thread sematics允许那些在单线程内不会改变单线程程序执行结果的重排序。


线程B访问到的是一个还未初始化的对象。

所以为了禁止这种优化,采用了volatile。

5、 静态内部类

class Singlenton05//适用场景:大对象,高并发,高性能
	private Singlenton05() 
	static class Inner
		private static Singlenton05 instance = new Singlenton05();
	
	public static Singlenton05 getSinglenton05() 
		return Inner.instance;
	

懒加载的好处:可以在使用对象的适合再去创建对象,可以节省内存。

6、 枚举

enum Instance06 
	instance06;
	private Instance06() 
	public static Instance06 getInstance06() 
		return instance06;
	

测试主方法

public class TestInstance 
	public static void main(String[] args) 
		Instance06 instance01 = Instance06.getInstance06();
		Instance06 instance02 = Instance06.getInstance06();
		System.out.println(instance01==instance02);
		System.out.println(instance01.hashCode());
		System.out.println(instance02.hashCode());
	




部分内容参考下链接
https://www.cnblogs.com/a154627/p/10046147.html









                                                ————  What is worth doing is worth doing well.

以上是关于六种单例模式的主要内容,如果未能解决你的问题,请参考以下文章

六种单例模式

[干货]设计模式:六种单例的创建方式,外加一大波Android进阶架构师资料分享

详解单例模式六种写法的优缺点

这 9 种单例模式你都会吗?

四种单例写法与测试

常见的六种设计模式以及应用场景