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("你好");
	}
	......
}

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

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • shell内部命令_rshell

    shell内部命令_rshellShell内值命令之readread读取控制台输入目标: 理解read命令的作用 使用read给多个变量赋值 使用read读取一个字符 使用read限制时间输入 介绍: read是shell内置命令,用于从标准输入中读取数据并赋值给变量,如果没有进行重定向,默认就是从终端控制台读取用户输入的数据,如果进行了重定向,那么可以从文件中读取数据. 语法:read[options][var1var2]options表示选项,如下所示,var表示用来存储数据的变量,可以是一个,也可以是多

  • App界面原型设计工具「建议收藏」

    App界面原型设计工具「建议收藏」首先,一款优秀的移动APP界面原型设计工具应该具备:  ①.支持移动端演示(随时随地演示给BOSS,厕所&食堂&电梯…以体现我是那么的敬业——长点工资必备)  ②.组件库(高效复用,谁用谁知道)  ③.可以快速生成全局流程(程序猿看不懂拆解的,给丫的看这个)  ④.在线协作(多个PM狗一起用)  ⑤.手势操作、转场动画、交互特效…(这些都不需要,留给专业的交互、视觉,

  • ai算法测试工程师面试_ai面试题

    ai算法测试工程师面试_ai面试题深度学习

  • java服务器开发心得

    java服务器开发心得本人已从事java服务器开发三年多了,对java服务器开发比较有心得,特此对这三年多来进行下技术总结,并与大家分享。作为服务器开发,对基础知识的掌握程度,将决定你的服务器各方面的能力,一般在进行java服务器开发前,最重要的是能够熟练运用以下技术:javaclassLoader、javathread、javaI/O(NIO)和javasocket。 一般来说,服务器设计大致

  • 三星识别文字_免费文字识别

    三星识别文字_免费文字识别百度AI实战营收官战(成都站),宣告百度OCR免费策略再次升级。百度通用文字识别服务的免费使用次数提升100倍,从每天500次提升至每天50000次;通用文字识别高精度版的免费使用次数提升10倍,从每天50次提升至每天500次。目前业界通常按照接口调用次数收费,单个接口单次调用费从几分钱到几毛钱不等,百度永久免费开放通用文字识别及其他文字识别技术,实实在在为企业节约一笔不菲的支出。现阶段已…

  • python替换文件的某个字符串_用Python替换文件中的字符串

    python替换文件的某个字符串_用Python替换文件中的字符串将所有这些代码放入一个名为mass_replace的文件中.在Linux或MacOSX下,您可以执行chmodxmass_replace,然后运行此操作.在Windows下,您可以使用pythonmass_replace后跟相应的参数来运行它.#!/usr/bin/pythonimportosimportreimportsys#listofextensionstorepl…

发表回复

您的电子邮箱地址不会被公开。

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