Immutability 模式:如何利用不变性解决并发问题?
不变性(Immutability)模式:对象一旦创建后状态不再变化。解决并发问题最简单的方法——让共享变量只有读操作。
快速实现具备不可变性的类
- 类和属性都是
final的,所有方法均是只读的 String、Long、Integer、Double等包装类均严格遵守此规则String.replace()不修改原value[],而是返回新字符串
不变性(Immutability)模式:对象一旦创建后状态不再变化。解决并发问题最简单的方法——让共享变量只有读操作。
final 的,所有方法均是只读的String、Long、Integer、Double 等包装类均严格遵守此规则String.replace() 不修改原 value[],而是返回新字符串Guava RateLimiter:Google 开源的限流工具类,基于令牌桶算法实现。
//限流器流速:2 个请求/秒
RateLimiter limiter = RateLimiter.create(2.0);
//执行任务的线程池
ExecutorService es = Executors.newFixedThreadPool(1);
//记录上一次执行时间
prev = System.nanoTime();
//测试执行 20 次
for (int i = 0; i < 20; i++) {
//限流器限流
limiter.acquire();
//提交任务异步执行
es.execute(() -> {
long cur = System.nanoTime();
//打印时间间隔:毫秒
System.out.println((cur - prev) / 1000_000);
prev = cur;
});
}
// 输出结果:
// ...
// 500
// 499
// 500
// 499

并发编程可以总结为三个核心问题:分工、同步、互斥。
已有 synchronized,还需要 Lock 的原因——需要锁支持以下能力以破坏不可抢占条件:
顾名思义,线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。
池化技术想必大家已经屡见不鲜了,线程池、数据库连接池、HTTP 连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池提供了一种限制和管理资源(包括执行一个任务)的方式。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
在 Java 中,锁可以按照 多个维度 进行分类,不同维度的锁适用于不同的并发场景。以下是详细的分类:
按锁的公平性划分
| 锁类型 | 特点 | 实现类/关键字 |
|---|---|---|
| 公平锁 | 严格按照线程请求顺序(FIFO)分配锁,避免线程饥饿,但性能较低。 | ReentrantLock(true) |
| 非公平锁 | 允许插队,新请求的线程可能直接抢到锁,吞吐量高,但可能导致线程饥饿(默认方式)。 | ReentrantLock(false)、synchronized |
Java 内存模型(Java Memory Model),简称 JMM。Java 内存模型的目标是为了解决由可见性和有序性导致的并发安全问题。Java 内存模型通过 屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
物理机遇到的并发问题与虚拟机中的情况有不少相似之处,物理机对并发的处理方案对于虚拟机的实现也有相当大的参考意义。
技术在进步,CPU、内存、I/O 设备的性能也在不断提高。但是,始终存在一个核心矛盾:CPU、内存、I/O 设备存在很大的速度差异 - CPU 远快于内存,内存远快于 I/O 设备。木桶短板理论告诉我们:一只木桶能装多少水,取决于最短的那块木板。同理,程序整体性能取决于最慢的操作(即 I/O 操作),所以单方面提高 CPU、内存的性能是无效的。
Java 并发编程中,分工工具是处理复杂并行任务的核心利器。当单个任务过于庞大或具有复杂的依赖关系时,我们需要将其拆分为多个子任务并行执行,最后汇总结果。Java 提供了多种分工工具来应对不同场景:ForkJoinPool 面向分治递归任务,CompletableFuture 面向异步编排与聚合,CompletionService 面向批量并行任务的结果收集。
对于简单的并行任务,你可以通过"线程池 + Future"的方案来解决;如果任务之间有聚合关系,无论是 AND 聚合还是 OR 聚合,都可以通过 CompletableFuture 来解决;而批量的并行任务,则可以通过 CompletionService 来解决。
【Java 并发】专题系统梳理多线程并发编程的核心知识体系,涵盖 Java 内存模型(JMM)、线程生命周期与同步机制、锁机制(synchronized/ReentrantLock/AQS)、无锁并发(CAS/原子类/ThreadLocal)等核心主题。同时深入线程池原理、并发容器、同步与分工工具类等实战内容,帮助理解并发编程的本质问题与解决方案。
原子性、可见性、有序性、死锁、上下文切换JMM、Happens-Before、volatile、synchronized、锁升级Thread、Runnable、Callable、线程生命周期、interruptReentrantLock、AQS、CAS、悲观锁、乐观锁CAS、原子类、ThreadLocal、ABA 问题、Copy-on-WriteAQS、CLH 队列、独占锁、共享锁、ConditionConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue、分段锁、CASThreadPoolExecutor、corePoolSize、拒绝策略、workQueue、ExecutorsCountDownLatch、Semaphore、CyclicBarrier、信号量、AQSCompletableFuture、ForkJoinPool、FutureTask、工作窃取、异步编排
- 什么是并发?
- 什么是并行?
- 并发和并行有什么区别?
并发和并行是最容易让新手费解的概念,那么如何理解二者呢?其最关键的差异在于:是否是同时发生: