Java 入门指南:Java 并发编程 —— 单例模式

2024-09-02 21:20

本文主要是介绍Java 入门指南:Java 并发编程 —— 单例模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

何为单例模式

单例模式是一种创建型的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点

在单例模式中,一个类只能有一个实例,并且该实例必须由类自身进行实例化和管理。它通常通过以下步骤实现:

  1. 私有化构造方法:将类的构造方法私有化,使其不能被外部类实例化。

  2. 创建一个私有的静态变量来保存类的唯一实例。

  3. 提供一个公有的静态方法 getInstance(),用于获取类的实例。在该方法中,首先检查实例是否已经存在,如果存在则直接返回该实例,如果不存在则创建一个新的实例。

单例模式常用于需要共享资源或控制某些资源的情况,例如数据库连接池、线程池、配置信息等。

它提供了一种方便的方式来管理和访问这些资源,同时也可以避免实例化过多造成的资源浪费和冲突。但单例模式也有一些潜在的问题,例如可能导致全局状态的不可控和单例的生命周期管理等,需要谨慎使用。

单例类型

饿汉单例模式

饿汉单例 是一种单例设计模式的实现方式,它的特点是在类加载时就创建实例,无论是否需要使用该实例。具体实现方式如下:

public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {// 私有构造方法,防止外部实例化}public static Singleton getInstance() {return instance;}
}

在上述代码中,Singleton 类的构造方法被声明为私有的,这样外部无法通过 new 关键字实例化该类。而类的静态变量 instance 被初始化为 Singleton 类的唯一实例,并且被声明为 final,保证了实例的唯一性。

由于在类加载时就会创建实例,并且通过静态方法 getInstance() 返回该实例,因此它是一种线程安全的单例模式实现。多个线程在访问 getInstance() 方法时只能获取到同一个实例

饿汉单例的缺点是无法延迟实例化,在类加载时就会创建实例,可能会浪费系统资源。如果应用程序在启动时就需要使用该实例,并且该实例的创建代价较低,那么饿汉单例是一种简单有效的实现方式。

但如果实例创建的代价较高或者不一定会被使用,就可以考虑其他延迟加载的单例模式实现方式,如懒汉单例或双重检查锁单例。

懒汉单例模式

懒汉单例 是一种延迟加载的单例设计模式实现方式,它的特点是在第一次使用时才创建实例。具体实现方式如下:

public class Singleton {private static Singleton instance;private Singleton() {// 私有构造方法,防止外部实例化}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

在上述代码中,Singleton 类的构造方法被声明为 private 的,这样外部无法通过 new 关键字实例化该类。而类的静态变量 instance 用于保存唯一的实例。

在静态方法 getInstance() 中,通过检查 instance 是否为 null 来判断是否需要创建实例。如果 instancenull,则在同步代码块中创建一个新的实例;否则,直接返回已存在的实例。

由于在懒汉单例中,实例的创建是在第一次调用 getInstance() 方法时进行的,因此可以避免在类加载时就创建实例,从而延迟实例化。但需要注意的是,懒汉单例在多线程环境下需要考虑线程安全性

上述代码中的 getInstance() 方法使用了 synchronized 关键字来实现线程安全,保证了多个线程在访问时只能顺序执行,避免了创建多个实例的问题。但使用 synchronized 关键字会引入一定的性能开销。

双重锁单例模式

双重检查锁单例 是一种延迟加载的单例设计模式实现方式,它在懒汉单例的基础上添加了双重检查机制,以提高性能和线程安全性。

public class Singleton {private static volatile Singleton instance;private Singleton() {// 私有构造方法,防止外部实例化}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

getInstance() 方法中,首先通过检查 instance 是否为 null 来避免不必要的同步开销。如果 instancenull,则进入同步代码块,并再次检查 instance 是否为 null,这是为了确保只有一个线程能够创建实例,这就是所谓的“双重锁检查机制”。

使用双重检查锁机制可以减少同步块的执行次数,提高了性能。同时,使用 volatile 关键字修饰实例变量 instance,可以保证多线程环境下的可见性和禁止指令重排序,避免了可能的线程安全问题。

volatile 关键字实现

需要注意的是,在早期的 Java 版本中,双重检查锁机制可能会出现问题,即使实例已经被创建出来,但由于指令重排序的原因,其他线程可能会获取到一个未完全初始化的实例。使用 volatile 关键字可以解决这个问题。

  • 使用 volatile 关键字是为了防止 instance = new Singleton() 这一步被指令重排序。实际分为三个子步骤:

    • 分配对象的内存空间。
    • 初始化对象。
    • 将 single 指向分配的内存空间
/* pseudocode */
a. memory = allocate() //分配内存
b. ctorInstanc(memory) //初始化对象
c. instance = memory   //设置instance指向刚分配的地址

如果不使用 volatile 关键字,JVM 可能会对这三个子步骤进行指令重排序,如果 if(null == instance)synchronized(Singleton.class) 被重排序,那么线程 A 可能在对象还没有被初始化完成时,线程 B 已经开始使用这个对象,从而导致问题。而使用 volatile 关键字可以防止这种指令重排序。

双重检查锁单例是一种在延迟加载和线程安全性之间做出权衡的实现方式,相比于简单的懒汉单例,它提供了更好的性能表现。因此,如果应用程序在启动时不需要立即创建实例,并且对性能有一定要求,那么双重检查锁单例是一种常用的实现方式。

这篇关于Java 入门指南:Java 并发编程 —— 单例模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131096

相关文章

Java NoClassDefFoundError运行时错误分析解决

《JavaNoClassDefFoundError运行时错误分析解决》在Java开发中,NoClassDefFoundError是一种常见的运行时错误,它通常表明Java虚拟机在尝试加载一个类时未能... 目录前言一、问题分析二、报错原因三、解决思路检查类路径配置检查依赖库检查类文件调试类加载器问题四、常见

Java注解之超越Javadoc的元数据利器详解

《Java注解之超越Javadoc的元数据利器详解》本文将深入探讨Java注解的定义、类型、内置注解、自定义注解、保留策略、实际应用场景及最佳实践,无论是初学者还是资深开发者,都能通过本文了解如何利用... 目录什么是注解?注解的类型内置注编程解自定义注解注解的保留策略实际用例最佳实践总结在 Java 编程

Python中模块graphviz使用入门

《Python中模块graphviz使用入门》graphviz是一个用于创建和操作图形的Python库,本文主要介绍了Python中模块graphviz使用入门,具有一定的参考价值,感兴趣的可以了解一... 目录1.安装2. 基本用法2.1 输出图像格式2.2 图像style设置2.3 属性2.4 子图和聚

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows