首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
当前位置:
首页>>
技术小册>>
Java并发编程实战
小册名称:Java并发编程实战
### 章节 32 | Balking模式:再谈线程安全的单例模式 在Java并发编程的广阔领域中,单例模式(Singleton Pattern)作为一种常用的设计模式,旨在确保一个类仅有一个实例,并提供一个全局访问点。然而,在多线程环境下实现单例模式时,必须谨慎处理线程安全问题,以避免出现多个实例或数据不一致的情况。本章将深入探讨Balking模式在实现线程安全单例模式中的应用,以及如何通过这种模式进一步优化和增强单例模式的健壮性和灵活性。 #### 一、Balking模式简介 Balking模式,又称为“退缩模式”或“犹豫模式”,是一种行为型设计模式。其核心思想是在执行某个操作之前,先检查该操作是否应该被执行。如果条件不满足(即“退缩”条件成立),则不执行该操作;如果条件满足,则正常执行。在单例模式的上下文中,Balking模式可以用来确保在特定条件下才创建单例对象,从而避免不必要的资源消耗或竞争条件。 #### 二、传统线程安全单例模式的回顾 在探讨Balking模式在单例模式中的应用之前,我们先简要回顾几种常见的线程安全单例实现方式: 1. **懒汉式(线程不安全)**:最简单的实现方式,但存在线程安全问题。 ```java public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 2. **懒汉式(线程安全,同步方法)**:通过在`getInstance()`方法上添加`synchronized`关键字来保证线程安全,但效率低下。 ```java public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 3. **双重检查锁定(Double-Checked Locking)**:一种优化方式,通过减少同步代码块的范围来提高性能。 ```java 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; } } ``` 4. **静态内部类**:利用JVM的类加载机制保证线程安全,且延迟加载。 ```java public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } ``` 5. **枚举方式**:最简洁且自动支持序列化机制,防止多次实例化。 ```java public enum Singleton { INSTANCE; public void someMethod() { // 方法实现 } } ``` #### 三、Balking模式在单例模式中的应用 Balking模式在单例模式中的应用,主要体现在对单例对象创建条件的灵活控制上。通过引入一个“退缩”条件,我们可以根据应用的需求或系统的状态来决定是否创建单例对象。这种机制特别适用于那些在某些条件下不需要单例对象,或者创建单例对象代价高昂的场景。 ##### 示例:条件性单例模式 假设我们有一个`DatabaseConnection`类,它代表数据库连接的单例。但在某些情况下(如数据库服务未启动或配置错误时),我们可能不希望创建这个单例对象。此时,可以使用Balking模式来避免不必要的创建尝试。 ```java public class DatabaseConnection { private static volatile DatabaseConnection instance; private static final boolean DATABASE_AVAILABLE = checkDatabaseAvailability(); // 假设这是一个检查数据库是否可用的方法 private DatabaseConnection() { // 初始化数据库连接 } private static boolean checkDatabaseAvailability() { // 这里模拟检查数据库是否可用的逻辑 // 实际应用中,可能需要连接数据库服务器进行验证 return true; // 假设数据库可用 } public static DatabaseConnection getInstance() { if (!DATABASE_AVAILABLE) { // 退缩条件:如果数据库不可用,则不创建实例 throw new IllegalStateException("Database is not available"); } if (instance == null) { synchronized (DatabaseConnection.class) { if (instance == null) { instance = new DatabaseConnection(); } } } return instance; } } ``` 在上述示例中,`DATABASE_AVAILABLE`变量作为“退缩”条件,用于在尝试创建单例对象之前进行检查。如果数据库不可用(即`DATABASE_AVAILABLE`为`false`),则`getInstance()`方法会抛出一个`IllegalStateException`,表明当前无法获取数据库连接实例。这种设计使得单例模式的实现更加灵活,能够根据系统的实际状态动态调整行为。 #### 四、Balking模式的优势与局限 **优势**: 1. **灵活性**:通过引入“退缩”条件,可以根据应用的需求或系统的状态动态决定是否创建单例对象,提高了代码的灵活性和适应性。 2. **性能优化**:在不需要创建单例对象的情况下,避免了不必要的资源消耗和初始化过程,从而提高了系统的性能。 3. **错误处理**:通过抛出异常或返回特定值,可以清晰地指示单例对象无法创建的原因,便于错误处理和调试。 **局限**: 1. **复杂性增加**:相比简单的单例实现方式,引入Balking模式会增加代码的复杂性和理解难度。 2. **条件判断开销**:每次调用`getInstance()`方法时都需要进行条件判断,虽然这种开销通常很小,但在高并发场景下仍需注意其对性能的影响。 3. **错误处理依赖**:如果“退缩”条件的检查逻辑有误或不够准确,可能会导致单例对象无法正确创建或错误地抛出异常。 #### 五、总结 Balking模式为线程安全的单例模式实现提供了一种灵活且强大的机制。通过引入“退缩”条件,我们可以根据应用的需求或系统的状态动态决定是否创建单例对象,从而提高了代码的灵活性和健壮性。然而,在使用Balking模式时,也需要注意其可能带来的复杂性增加和性能开销问题。因此,在实际应用中应根据具体情况权衡利弊,选择最适合的实现方式。
上一篇:
31 | Guarded Suspension模式:等待唤醒机制的规范实现
下一篇:
33 | Thread-Per-Message模式:最简单实用的分工方法
该分类下的相关小册推荐:
Java语言基础16-JDK8 新特性
Java语言基础15-单元测试和日志技术
Java语言基础7-Java中的异常
Mybatis合辑5-注解、扩展、SQL构建
Java并发编程
Java语言基础11-Java中的泛型
java源码学习笔记
JAVA 函数式编程入门与实践
SpringBoot合辑-初级篇
深入拆解 Java 虚拟机
Java语言基础3-流程控制
SpringBoot零基础到实战