Java学习笔记 - 单例模式
概述
单例模式是一种创建者模式。当我们需要确保系统中某个类仅能存在一个对象时,比如:全局信息类例如当项目启动时我们将一个配置文件读取为一个Config类的实例从而在业务逻辑中通过操作对象读取配置、无状态的工具类仅需一个实例进行复用即可,也就是当该对象仅需一个实例即可或处于安全考虑而做出的限制并且反复创建该实例会消耗系统资源,此时可以使用单例模式。
实现方法
在实现一个单例类时,由于涉及到类加载、对象属性的创建、对象属性的访问等问题,需要考虑到对象创建时间、创建与赋值的方式、线程安全、阻止反射与对象序列化造成的被多例等情况。
Eager 饿汉式
饿汉式是最简单的单例模式,类中的对象属性在类加载时就被初始化,由于JVM加载类时保证单线程,所以避免了线程问题,但eager加载方式造成即使运行过程中全程未使用类该类也会被加载,造成不必要的资源浪费。
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton {}
public static Singleton getInstance() {
return singleton;
}
}
等同代码
public class Singleton {
private static Singleton singleton;
static {
singleton = new Singleton();
}
private Singleton {}
public static Singleton getInstance() {
return singleton;
}
}
Lazy 懒加载
懒加载即在使用时对单例类进行实例化,但简单的懒加载未考虑线程安全问题,在getInstance()方法中,若两个线程同时进入实例对象等于null的if语句中,对象将会被实例化两次从而违背单例模式的初衷。
// 线程不安全的懒加载单例类代码
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
// 重量级锁线程安全的懒加载单例类实现
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
// 方法内部锁线程安全的懒加载单例类实现(双重检查)
public class Singleton {
// 由于实例化对象非原子操作,所以加volatile
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
// 第一次判断让实例构造完成后能并发执行
if(singleton == null) {
synchronized (Singleton.class) {
// 第二次判断防止第一次判断同时进入多个线程
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
// 静态内部类懒汉式写法
public class Singleton {
private Singleton() {}
// 使用了静态内部类,让JVM在类加载也就是内部类被Singleton.getInstance()方法内调用时时为我们初始化
private static class SingletonInstance {
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.singleton;
}
}
Enum 枚举式
枚举式就是用java中枚举类的形式构造的单例类,枚举式在保证能懒加载线程安全(枚举对象是以static形式初始化,JVM保证线程安全)的同时,由于java中规定了在枚举对象序列化时仅输出name,而反序列化时使用name查找对象,从而实现了单例而非被破坏。在反射中,由于newInstance()在用户试图创建enum类型的对象时会检查从而报错,所以十分安全。
// 枚举式实现Resource的单例,通过Something.INSTANCE.getInstance()即可访问
class Resource{
}
public enum Something {
INSTANCE;
private Resource instance;
Something() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
总结
结合多方因素,能同时实现懒加载、线程安全、阻止序列化反序列化与反射破坏单例就是最佳的枚举式单例模式实现。
Java学习笔记 - 单例模式的更多相关文章
- Java学习笔记4
Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 0035 Java学习笔记-注解
什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...
- Java学习笔记(04)
Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...
- 0032 Java学习笔记-类加载机制-初步
JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...
- 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用
垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...
- 0028 Java学习笔记-面向对象-Lambda表达式
匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...
- 0025 Java学习笔记-面向对象-final修饰符、不可变类
final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
随机推荐
- 轻松处理高于平常10倍的视频需求,还能节省60%的IT成本,蓝墨做对了什么?
近年来,Serverless 一直在高速发展,并呈现出越来越大的影响力.主流的云服务商也在不断地丰富云产品体系,提供更好的开发工具,更高效的应用交付流水线,更好的可观测性,更细腻的产品间集成,但一切才 ...
- [Go] go build 和 go install 的区别
$ go build 源文件及其包依赖 编译成二进制. install 不仅执行build过程 而且会把编译的二进制放到 $GOPATH/bin/,包放到 $GOPATH/pkg/ Link:http ...
- dotnet 使用增量源代码生成技术的 Telescope 库导出程序集类型
本文将告诉大家在 dotnet 里面使用免费完全开源的基于增量源代码生成技术的 Telescope 库,进行收集导出项目程序集里面指定类型.可以实现性能极高的指定类型收集,方便多模块对接入自己的业务框 ...
- dotnet 调试应用启动闪退的方法
应用程序如果启动即闪退,那大部分时候日志模块还没初始化完成,很难通过应用自身的启动流程了解到应用启动失败的原因.本文来告诉几个不同的方法用来调查应用启动失败的原因 应用启动失败的原因可能有很多,例如系 ...
- 3种方式自动化控制APP
自动化控制APP不管是在工作还是生活方面,都可以帮助我们高效地完成任务,节省时间和精力.本文主要介绍自动化控制APP的3种常用方式. 1.Python + adb 这种方式需要对Android有一些基 ...
- 11.Node节点维护
题目:Node节点维护 配置环境kubectl config use-context ek8s 将名为ek8s-node-0的node节点设置为不可用,并重新调度该node上所有运行的pods. 官方 ...
- k8s-nginx实战部署1
目录 yaml 资源清单 run_deploy.sh .gitlab-ci.yml yaml 资源清单 deploy.yaml apiVersion: v1 kind: ConfigMap metad ...
- Linux中的man page指令
以Linux上的date命令为例,在控制台输入 man date,将会展示如下界面: [vbird@www ~]$ man date DATE(1) User Commands DATE(1) # 请 ...
- Oracle删除列操作:逻辑删除和物理删除
概念 逻辑删除:逻辑删除并不是真正的删除,而是将表中列所对应的状态字段(status)做修改操作,实际上并未删除目标列数据或恢复这些列占用的磁盘空间.比如0是未删除,1是删除.在逻辑上数据是被删除了, ...
- AIRIOT答疑第2期|如何使用物联网平台的数据采集与控制引擎?
任性用! 作为AIRIOT物联网低代码平台的五大核心能力引擎之一,数据采集与控制引擎具备极强的系统集成能力,提供丰富的接口,具备海量工业设备驱动库,分布式采集,稳定性高,实现快速的设备接入.报警. ...