概述

单例模式是一种创建者模式。当我们需要确保系统中某个类仅能存在一个对象时,比如:全局信息类例如当项目启动时我们将一个配置文件读取为一个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()方法中,若两个线程同时进入实例对象等于nullif语句中,对象将会被实例化两次从而违背单例模式的初衷。

//  线程不安全的懒加载单例类代码
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学习笔记 - 单例模式的更多相关文章

  1. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  2. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  3. 0035 Java学习笔记-注解

    什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...

  4. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  5. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  6. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  7. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  8. 0025 Java学习笔记-面向对象-final修饰符、不可变类

    final关键字可以用于何处 修饰类:该类不可被继承 修饰变量:该变量一经初始化就不能被重新赋值,即使该值跟初始化的值相同或者指向同一个对象,也不可以 类变量: 实例变量: 形参: 注意可以修饰形参 ...

  9. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  10. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

随机推荐

  1. 阿里巴巴开源大规模稀疏模型训练/预测引擎DeepRec

    ​简介:经历6年时间,在各团队的努力下,阿里巴巴集团大规模稀疏模型训练/预测引擎DeepRec正式对外开源,助力开发者提升稀疏模型训练性能和效果. ​ 作者 | 烟秋 来源 | 阿里技术公众号 经历6 ...

  2. 编译优化 | LLVM代码生成技术详解及在数据库中的应用

    简介: 作者:长别 1. 前言 随着IT基础设施的发展,现代的数据处理系统需要处理更多的数据.支持更为复杂的算法.数据量的增长和算法的复杂化,为数据分析系统带来了严峻的性能挑战.近年来,我们可以在数据 ...

  3. [FAQ] WPS 服务程序是一种流氓软件吗

    是的,周而复始的后台进程,频率大致是每隔一个小时会运行一个购物车图标的后台程序,点击之后就会打开电商网站,随后这个程序消失.再出现. 当前时间:2021-10-29 Other:[FAQ] 你所看过的 ...

  4. dotnet C# 调用委托的 GetInvocationList 的对象分配

    本文也叫跟着 Stephen Toub 大佬学性能优化系列,这是我从 Stephen Toub 大佬给 WPF 框架做性能优化学到的知识,在热路径下,也就是频繁调用的模块,如果调用了委托的 GetIn ...

  5. FPGA最大工作频率教程

    FPGA最大工作频率教程 1.  Quartus的时序分析 作为编译过程的一部分,Quartus对布局布线的电路做时序分析.在编译报告里,展开"Timing Analyzer",这 ...

  6. 006_Orcad创建Hetergeneous分裂元件

    006_Orcad创建Hetergeneous分裂元件 以169脚的EMMC为例: 分为两部分,用到的引脚和NC的引脚. 先画一个框,依据引脚功能添加引脚.A部分做好,做B部分.引脚多,可以用pin ...

  7. 实验8 #第8章 Verilog有限状态机设计-3 #Verilog #Quartus #modelsim

    3. 状态机A/D采样控制电路 3.1 目标:用状态机控制ADC0809实现数据采集. 3.2 ADC0809简介 (1)ADC0809是8位A/D转换器,片内有8路模拟开关,可控制8个 模拟量中 的 ...

  8. ruby http请求组件

    github地址 https://github.com/rest-client/rest-client gemfile里添加 gem 'rest-client', '~> 2.0' 执行 bun ...

  9. Javascript返回顶部和砸金蛋,跑马灯等游戏代码实现

    1. 我们经常写页面的时候会遇到页面很长需要做返回顶部的操作:$("id /class").animate({scrollTop:$('.class').offset().top} ...

  10. postgresql性能优化1:min和max的性能

    select max(datatime) as id from mytable ---全表检索,时间慢执行时间5分钟 select max(datatime) as id from mytable w ...