概述

单例模式是一种创建者模式。当我们需要确保系统中某个类仅能存在一个对象时,比如:全局信息类例如当项目启动时我们将一个配置文件读取为一个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. 【pytorch学习】之自动微分

    5 自动微分 求导是几乎所有深度学习优化算法的关键步骤.虽然求导的计算很简单,只需要一些基本的微积分.但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错).深度学习框架通过自动计算导数 ...

  2. MaxCompute笛卡尔积逻辑的参数优化&复杂JOIN逻辑优化

    简介: 这篇文章主要讲一个SQL优化反映的两个优化点.分别是: 一.笛卡尔积逻辑的参数优化. 二.一个复杂JOIN逻辑的优化思路. 1.  优化概述 最近协助一个项目做下优化任务的工作.因为主要数据都 ...

  3. 五分钟学会使用 go modules(含在家办公使用技巧)

    导读:go modules 是 golang 1.11 新加的特性.如今 1.13 都已经发布了第 7 个小版本了,几乎所有大项目均已开始使用,这自然也包括 Kubernetes 生态中的众多项目.笔 ...

  4. Java异步非阻塞编程的几种方式

    简介: Java异步非阻塞编程的几种方式 一. 从一个同步的Http调用说起 一个很简单的业务逻辑,其他后端服务提供了一个接口,我们需要通过接口调用,获取到响应的数据. 逆地理接口:通过经纬度获取这个 ...

  5. 深入浅出FlatBuffers原理

    简介: FlatBuffers 是一个开源的.跨平台的.高效的.提供了多种语言接口的序列化工具库.实现了与 Protocal Buffers 类似的序列化格式.主要由 Wouter van Oortm ...

  6. Flink 1.14 新特性预览

    ​简介: 一文了解 Flink 1.14 版本新特性及最新进展 本文由社区志愿者陈政羽整理,内容源自阿里巴巴技术专家宋辛童 (五藏) 在 8 月 7 日线上 Flink Meetup 分享的<F ...

  7. [FE] uni-app 导航栏开发指南

    一种是 原生导航栏添加自定义按钮.简单明了. pages.json 配置 { "path": "pages/log/log", "style" ...

  8. Python 潮流周刊#48:Python 3.14 的发布计划

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  9. [WC/CTS2024] 线段树 题解

    Link 纪念一下场切题. 题意:给定一棵(分点不一定为中点)的线段树,给定若干个询问区间,问有多少个线段树上结点的集合,知道了这些结点对应的区间和就可以知道任何一个询问区间的和. 从询问区间开始考虑 ...

  10. 更新package.json里所有模块

    安装该插件 cnpm install -g npm-check-updates 或者 npm install -g npm-check-updates 在有package.json的目录执行 npm- ...