在Java并发编程中,volatile关键字是一种用于多线程环境下保证变量访问安全的重要机制。其核心作用主要体现在以下两个方面:
1. 可见性保障
当一个变量被声明为volatile时,任何对该变量的写操作都会立即刷新到主内存中,而读操作也会直接从主内存读取最新值,从而确保了不同线程之间对该变量修改的可见性。
2. 禁止指令重排
volatile通过插入内存屏障的方式,防止编译器和处理器对相关代码进行重排序优化,从而保证程序执行顺序与代码编写顺序一致,尤其在单例模式等场景下起到关键作用。
[此处为图片1]
CAS(Compare-And-Swap)是实现无锁并发控制的核心原子操作之一。它基于硬件层面的支持,在不使用传统互斥锁的前提下完成线程安全的数据更新。
1. 原子性实现原理
CAS操作涉及三个参数:内存地址V、预期旧值A和要更新的新值B。只有当内存位置当前的实际值等于预期值A时,才会将该位置更新为B,否则不做任何操作。整个过程是原子的,由CPU指令直接支持。
2. 优势与局限
相比加锁机制,CAS避免了线程阻塞和上下文切换带来的开销,提升了高并发下的系统吞吐量。但其存在ABA问题——即数据先由A变为B再变回A,可能导致误判。可通过引入版本号或时间戳机制(如AtomicStampedReference)加以解决。
[此处为图片2]
Java中的synchronized关键字提供了一种内置的同步机制,用于控制多个线程对共享资源的访问。
1. 互斥访问控制
synchronized可修饰实例方法、静态方法或代码块,确保同一时刻仅有一个线程能够进入临界区执行,从而实现线程间的互斥。
2. 锁类型与可见性
对于实例方法,锁对象是当前实例;对于静态方法,则锁定的是类的Class对象。此外,获取synchronized锁的同时会清空本地内存中的变量副本,从主内存重新加载,因此也具备内存可见性保障。
3. JVM层的锁优化
在JVM实现中,synchronized并非始终表现为重量级锁,而是根据竞争情况动态升级:从无锁状态 → 偏向锁 → 轻量级锁 → 重量级锁,这一过程有效减少了低竞争环境下的性能损耗。
[此处为图片3]
Lock接口作为java.util.concurrent.locks包中的核心组件,提供了比synchronized更细粒度和灵活的锁控制方式。
1. 显式锁管理
与synchronized隐式获取释放不同,Lock需要手动调用lock()和unlock()方法,虽然增加了复杂度,但也带来了更高的控制能力。
2. 功能扩展性强
- 支持中断响应:线程在等待获取锁时可以被中断。
- 支持超时获取:tryLock(long time, TimeUnit unit)允许设定等待时限。
- 支持公平性选择:ReentrantLock可在构造时指定是否采用公平策略,减少线程“饥饿”现象。
3. 条件变量协作
Lock可结合Condition实现精确的线程等待/唤醒机制,支持多个等待队列,适用于复杂的线程协调场景。
[此处为图片4]
ReentrantLock是Lock接口的一个重要实现,与synchronized相比具有更多高级特性:
1. 实现层级差异
synchronized是JVM层面的关键字,底层由C++实现;而ReentrantLock是基于Java语言开发的标准类库API,逻辑位于用户态。
2. 公平性可控
ReentrantLock允许通过构造函数传参设置为公平锁,确保等待时间最长的线程优先获得锁,而synchronized仅支持非公平模式。
3. 更丰富的操作接口
除了基本加锁功能外,还支持查询锁状态、获取等待线程列表、尝试获取锁等多种诊断和控制手段。
4. 复杂协作支持
借助Condition,ReentrantLock可以实现分组唤醒线程,例如生产者-消费者模型中的signalAllForQueue等精细化控制。
[此处为图片5]
线程池是一种有效的线程资源管理机制,广泛应用于Java后台服务中以提升性能和稳定性。
1. 核心价值
通过预先创建并维护一组可复用的线程,避免频繁地创建和销毁线程所带来的系统开销,同时限制并发线程数量,防止资源耗尽。
2. 使用方式
Java通过Executor框架提供便捷的线程池创建方式,常用工具类Executors提供了多种预设策略:
- newFixedThreadPool:固定大小线程池
- newCachedThreadPool:可缓存线程池
- newScheduledThreadPool:支持定时及周期任务
- newSingleThreadExecutor:单线程池
实际提交任务时,只需将实现了Runnable或Callable接口的对象传递给execute()或submit()方法即可。
[此处为图片6]
CountDownLatch与CyclicBarrier均为JUC包中常用的同步辅助类,但用途和行为有显著区别:
1. 应用场景不同
CountDownLatch通常用于“一个或多个线程等待其他线程完成”,比如主线程等待所有工作线程初始化完毕后再继续执行;而CyclicBarrier则是“多个线程相互等待彼此到达某个点后共同继续”,常用于并行计算中的阶段性同步。
2. 可重复使用性
CountDownLatch是一次性的,计数器一旦归零便不可恢复;而CyclicBarrier在所有线程通过屏障后可自动重置计数,支持重复使用。
3. 中断处理机制
两者都支持线程中断,但在处理方式上略有差异,CyclicBarrier在检测到中断时会立即打破屏障并抛出异常。
[此处为图片7]
Java内存模型(JMM)定义了happens-before原则,这是理解并发程序可见性和有序性的基础规则。
1. 规则含义
如果一个操作A happens-before 操作B,则意味着A的结果对B是可见的,并且A的执行顺序在B之前。
2. 典型示例
- 程序顺序规则:同一个线程中的操作遵循代码顺序。
- volatile变量规则:对一个volatile变量的写操作 happens-before 后续对该变量的读操作。
- 监视器锁规则:解锁操作 happens-before 随后对同一锁的加锁操作。
这些规则共同构成了JMM中保证多线程环境下正确数据传递的基础。
[此处为图片8]
Java中的ReadWriteLock是一种同步机制,它将读取和写入操作分开处理,允许多个线程同时进行读操作,但只允许一个线程进行写操作。
2、性能提升原理:
通过允许多个读线程并发访问共享资源,而仅在写入时独占锁,从而提高了高并发场景下读多写少的应用性能。例如,在缓存系统或配置管理器中,这种锁机制可以显著减少线程等待时间。
[此处为图片1]
CountDownLatch的作用与使用方式:
1、基本概念:
CountDownLatch是Java中的一个同步辅助类,用于让一个或多个线程等待其他线程完成一系列操作后再继续执行。
2、内部机制:
其核心是一个计数器,初始化时设定某个数值。每当一个任务线程调用countDown()方法时,计数器减一;而调用await()方法的线程会一直阻塞,直到计数器归零。
3、典型应用场景:
常用于主线程等待多个子线程完成准备工作后统一开始执行,或者协调多个服务启动顺序等。
CyclicBarrier的定义及其特点:
1、基本功能:
CyclicBarrier是一种线程同步工具,用于使一组线程相互等待,直到全部到达某个公共屏障点(即“集合点”),然后一起释放继续执行。
2、可重用性优势:
与CountDownLatch不同,CyclicBarrier在所有线程被释放后可以被重置并重复使用,适用于循环执行的任务场景。
3、额外行为支持:
当所有线程都到达屏障时,CyclicBarrier可以选择性地执行一个预设的Runnable任务,比如汇总数据或记录日志。
[此处为图片2]
4、与CountDownLatch的主要区别:
- CountDownLatch是一次性的,计数器一旦归零便不可再用;而CyclicBarrier可重置,支持多次使用。
- CountDownLatch通常用于“等待事件发生”,而CyclicBarrier更侧重于“线程之间互相等待”。
- 方法设计上:CountDownLatch依赖countDown()和await();CyclicBarrier主要通过await()实现等待逻辑。
Semaphore在并发控制中的作用:
1、概念说明:
Semaphore(信号量)是一种用于控制同时访问特定资源的线程数量的同步工具,本质上是一个计数器。
2、工作方式:
线程通过acquire()方法尝试获取许可,若当前无可用许可则阻塞;任务完成后需调用release()释放许可,以便其他线程使用。
3、常见用途:
可用于限制对有限资源的并发访问,如数据库连接池大小控制、文件读写并发数限制等。
[此处为图片3]
Java中如何安全地终止线程?
1、中断机制(推荐方式):
通过调用线程的interrupt()方法设置中断标志,目标线程应定期检查Thread.currentThread().isInterrupted()状态,并据此有序退出执行流程。
2、使用标志变量:
定义一个volatile类型的布尔变量作为运行开关,线程循环检测该标志位是否被外部修改,若为false则停止运行。
3、利用Future.cancel()方法:
对于由线程池提交的任务,可以通过返回的Future对象调用cancel(true)来尝试中断正在运行的线程,实现安全关闭。
4、避免使用已废弃的方法:
不应使用Thread类中的stop()方法强行终止线程,因其可能导致资源未释放、数据不一致等问题,属于不安全操作。
ReentrantLock与synchronized的区别分析:
1、功能灵活性:
ReentrantLock相比synchronized提供了更高的控制粒度,支持尝试获取锁(tryLock)、带超时的锁获取以及可中断的锁等待。
2、公平性选择:
ReentrantLock可通过构造函数指定是否采用公平策略,确保等待最久的线程优先获得锁;而synchronized始终是非公平的。
3、条件等待机制:
ReentrantLock结合Condition接口可以创建多个条件队列,实现精细化的线程通知机制,而synchronized只能配合Object的wait/notify使用单一等待集。
4、运行时状态查询:
ReentrantLock允许程序主动查询当前锁的状态,例如是否有线程持有锁、有多少线程在等待等,便于调试和监控。
ThreadLocal的作用及适用场景:
1、核心作用:
ThreadLocal为每个使用该变量的线程提供独立的变量副本,使得每个线程都可以独立地改变自己的副本而不影响其他线程。
2、典型应用:
适用于需要隔离线程间状态的场景,如Web应用中保存用户会话上下文、数据库连接、事务上下文信息等,避免显式传递参数且保证线程安全性。
Java线程池的功能与性能优化机制:
1、资源复用机制:
线程池预先创建一定数量的线程并重复利用,避免频繁创建和销毁线程带来的系统开销。
2、并发控制能力:
通过设定核心线程数、最大线程数等参数,有效控制应用程序的并发水平,防止资源过度占用。
3、快速响应任务:
由于线程已预先存在,新提交的任务可以直接分配给空闲线程执行,无需等待线程初始化过程。
4、增强功能支持:
提供任务排队策略、延迟执行、周期性任务调度、线程池状态监控等功能,提升系统的可管理性和稳定性。
ReadWriteLock 的基本机制与特性:
ReadWriteLock 维护了一对关联的锁:一个共享锁用于读操作,允许多个线程同时进行读取;另一个排他锁用于写操作,确保写入时其他读写线程被阻塞。这种设计实现了读写分离。
在读多写少的应用场景中,该锁机制显著提升了并发性能。相比 synchronized 的互斥访问模式,ReadWriteLock 允许多个读线程并发执行,仅在写操作发生时才进行阻塞,从而提高了整体吞吐量。
[此处为图片1]
ThreadLocal 的定义与工作机制:
ThreadLocal 用于创建线程局部变量,每个使用该变量的线程都会拥有其独立的副本实例,彼此之间互不干扰。
其实现原理是为每个线程维护一个独立的数据副本,达到线程间数据隔离的目的。常见应用场景包括存储用户会话信息、事务上下文或线程级别的缓存等。
需要注意的是,若未正确管理 ThreadLocal 变量,可能会引发内存泄漏问题。由于其内部使用弱引用但仍可能持有强引用链,建议在使用完毕后显式调用 remove() 方法释放资源。
synchronized 与 volatile 的区别分析:
在 Java 并发编程中,synchronized 和 volatile 各有不同用途。
synchronized 提供了互斥访问能力,可作用于代码块或方法,保证同一时刻只有一个线程能进入临界区,有效防止竞争条件,实现线程同步。
volatile 则侧重于保证变量的内存可见性。当一个变量被声明为 volatile,任何对该变量的修改都会立即刷新到主内存,并使其他线程的本地副本失效,从而保证最新值的可见性。但它不保证复合操作的原子性,因此不能完全替代 synchronized。
Thread.join() 方法的功能说明:
join() 方法允许当前线程等待目标线程完成执行后再继续运行,常用于控制线程执行顺序。
例如,在主线程中启动子线程并调用其 join() 方法,主线程将暂停执行,直到子线程结束。这有助于实现有序的流程控制和结果依赖处理。
[此处为图片2]
wait() 与 notify() 的使用方式:
wait() 方法使当前线程进入等待状态,直至其他线程在相同对象上调用 notify() 或 notifyAll() 方法将其唤醒。
notify() 用于唤醒在该对象监视器上等待的一个线程(具体哪一个由 JVM 调度决定),而 notifyAll() 则唤醒所有等待线程。
这两个方法必须在同步块或同步方法内调用,否则会抛出 IllegalMonitorStateException 异常。此外,调用 wait() 时会自动释放持有的锁,而 notify() 唤醒线程后并不会立即释放锁,需等待退出同步块后才能让出。
Exchanger 工具类的作用解析:
Exchanger 是 Java 中用于线程间协作的工具类,提供了一个交换点,使得两个线程可以在该点交换各自持有的数据。
它适用于成对线程之间的数据传递场景,如生产者-消费者模型中的配对交换。当一个线程调用 exchange() 方法后会阻塞,直到另一个线程也调用相同方法完成数据交换。
StampedLock 的核心特点:
StampedLock 是 JDK 8 引入的一种高性能读写锁实现,支持三种模式:写锁、悲观读锁和乐观读锁。
相较于传统读写锁,它支持锁的升级与降级机制——即从读锁升级为写锁,或从写锁降级为读锁,增强了灵活性。
其中,乐观读锁是一种轻量级机制,在无写操作干扰时允许非阻塞读取,极大提升了高并发读场景下的性能表现。
Future 与 Callable 的优势体现:
在并发任务处理中,Callable 接口代表一个可返回结果的任务,而 Future 用于获取该任务的执行结果,二者结合实现了异步计算。
通过 Future.get() 可以获取 Callable 执行后的返回值,若任务尚未完成则会阻塞等待。该方法还支持设置超时时间,避免无限期等待。
此外,Future 能够捕获任务执行过程中抛出的异常,便于统一进行错误处理,提升程序健壮性。
Thread 类与 Runnable 接口的主要差异:
Thread 是一个具体的类,通过继承它可以创建线程,但 Java 不支持多重继承,因此一旦类已继承其他父类,则无法再继承 Thread。
Runnable 是一个函数式接口,通过实现该接口并传入 Thread 实例,可以更灵活地定义线程任务。这种方式更适合需要多线程共享同一任务实例的场景。
从设计角度看,实现 Runnable 更符合“组合优于继承”的原则,也便于线程任务的复用与解耦。
Java 内存模型(JMM)对并发的影响:
Java 内存模型(JMM)规范了多线程环境下变量的可见性、原子性和有序性行为。
可见性方面,JMM 定义了一个线程对共享变量的修改何时对其他线程可见,volatile 关键字正是基于此机制实现即时可见。
原子性方面,JMM 规定了某些基本操作(如对 long 和 double 以外的基本类型赋值)是不可分割的。对于复合操作,则需借助 synchronized 或 atomic 类来保证。
顺序性通过 happens-before 原则来保障,即使存在指令重排序优化,也能确保程序逻辑的正确执行顺序。
Executor 框架的优势概述:
Executor 框架是 Java 并发包中用于简化线程管理的核心组件之一。
它通过提供线程池机制,统一管理线程的创建、调度与回收,降低了直接使用 new Thread() 带来的资源开销。
借助线程复用机制,减少了频繁创建和销毁线程带来的性能损耗,提升了系统响应速度和资源利用率。
同时,该框架提供了丰富的调度策略和任务控制功能,如定时执行、周期执行、任务提交与取消等,增强了并发程序的可控性和可维护性。
在Java并发编程中,保证操作的原子性是确保多线程环境下数据一致性的关键。实现方式主要包括以下几种:
1、synchronized关键字: 通过同步方法或代码块对共享资源进行加锁,使得同一时刻只有一个线程可以执行该段代码,从而保障操作的原子性。
2、Lock接口机制: 使用如ReentrantLock等显式锁来控制线程访问临界区,提供比synchronized更灵活的锁定策略,例如可中断等待和超时获取锁。
3、原子变量类:
利用java.util.concurrent.atomic包下的原子类型(如AtomicInteger、AtomicLong等),借助CAS(Compare-And-Swap)算法实现无锁化的原子操作,提升性能的同时保证线程安全。
[此处为图片1]
为了应对高并发场景,Java提供了多种线程安全的集合类,这些并发集合类在设计上优化了传统同步容器的性能瓶颈:
1、ConcurrentHashMap: 采用分段锁(JDK 8后改为CAS + synchronized)技术,允许多个线程同时读写不同段的数据,显著提高了并发访问效率。
2、CopyOnWriteArrayList: 在修改操作时复制整个底层数组,读操作无需加锁,适用于读远多于写的并发场景,但写操作开销较大。
3、BlockingQueue: 阻塞队列支持插入和移除操作的阻塞特性。当队列满时插入被阻塞,队列空时提取被阻塞,非常适合用于生产者-消费者模型中的线程协调。
其中,SynchronousQueue是一种特殊的阻塞队列——它不存储元素。每个插入操作必须等待另一个线程的对应移除操作,反之亦然,因此常用于直接传递任务或数据,适用于高吞吐的任务调度场景。
[此处为图片2]
关于线程间的协作机制,wait()与notify()方法是Object类提供的基础工具,主要用于线程间通信:
- wait():使当前线程释放对象监视器并进入等待状态,直到其他线程调用相同对象上的notify()或notifyAll()方法。
- notify()/notifyAll():唤醒一个或所有在该对象上等待的线程,使其重新竞争锁。
- 这两个方法必须在synchronized修饰的方法或代码块中调用,否则会抛出IllegalMonitorStateException异常。
[此处为图片3]
Java中的AQS(AbstractQueuedSynchronizer)是构建锁和同步组件的核心框架,其工作原理如下:
- 状态管理: 内部维护一个volatile int类型的state变量,表示同步状态(如锁是否已被占用)。
- 两种模式: 支持独占模式(Exclusive Mode)和共享模式(Shared Mode),分别对应如ReentrantLock和Semaphore等实现。
- 等待队列: 使用FIFO队列管理争抢同步状态失败的线程,确保线程按顺序获取资源,避免饥饿。
基于AQS,JUC包实现了众多同步工具,包括ReentrantLock、CountDownLatch、Semaphore等,极大简化了开发者自定义同步器的过程。
[此处为图片4]
Fork/Join框架是Java 7引入的一种并行计算框架,专为处理可分解的大任务而设计:
- 遵循“分而治之”思想,将大任务递归地拆分为若干小任务(fork阶段),并行执行后合并结果(join阶段)。
- 核心是ForkJoinPool线程池,其采用工作窃取算法(Work-Stealing Algorithm):空闲线程从其他繁忙线程的任务双端队列尾部“窃取”任务执行,有效平衡负载,提高CPU利用率。
该框架特别适合用于数值计算、大数据遍历、树形结构处理等可分割的并行任务。
[此处为图片5]
死锁是多线程编程中常见的问题,指两个或多个线程因相互持有对方所需的锁而导致永久阻塞的现象。典型的产生条件包括互斥、占有并等待、不可抢占和循环等待。
避免死锁的主要策略有:
- 尽量减少一个线程同时持有多个锁的情况。
- 以固定的顺序申请多个资源,打破循环等待条件。
- 设置锁获取的超时时间,使用tryLock(long timeout)等方式尝试获取锁,失败则回退释放已有资源。
此外,ThreadLocalRandom是针对多线程环境下随机数生成的优化实现。它是Random的子类改进版,通过为每个线程维护独立的随机种子,避免了多线程竞争同一实例带来的性能下降,提升了高并发下随机数生成的效率和安全性。
[此处为图片6]
Java中的原子类是一组在并发环境中能够保证操作原子性的工具类,常见的包括AtomicInteger、AtomicLong和AtomicBoolean等。这些类通过底层的CAS(比较并交换)机制实现线程安全,无需使用传统的锁结构,从而在高并发场景下提供更高的性能。
其主要用途在于实现无锁的线程安全操作,广泛应用于计数器、状态标志位更新以及共享变量的高效管理中。相比使用synchronized或Lock加锁的方式,原子类减少了线程阻塞和上下文切换的开销。
[此处为图片1]
CompletableFuture是Java 8引入的一个用于支持异步编程的重要类。它允许开发者以非阻塞的方式执行任务,并通过链式调用组合多个异步操作,极大地提升了代码的可读性和维护性。
借助函数式编程特性,可以使用Lambda表达式简洁地定义回调逻辑。例如,可以轻松实现任务完成后的处理、异常捕获、结果转换与合并等操作,使复杂的异步流程变得清晰易懂。
Executor框架是Java 5引入的一套用于管理和调度线程资源的核心工具,旨在提升多线程程序的性能和可维护性。该框架基于线程池机制,避免了频繁创建和销毁线程所带来的系统开销。
其核心接口为Executor及其扩展接口ExecutorService,常用的实现类包括ThreadPoolExecutor和ScheduledThreadPoolExecutor,分别用于普通任务执行和定时/周期性任务调度。
Phaser是Java并发包中的一种同步辅助类,类似于CyclicBarrier和CountDownLatch,但具有更高的灵活性。它可以作为可重用的屏障,支持动态注册和注销参与线程的数量,适用于需要分阶段协调线程执行的场景。
与CyclicBarrier相比,后者仅适用于固定数量的线程相互等待;而Phaser允许运行时调整线程数量。相对于CountDownLatch,Phaser不是一次性的,可以在多个阶段重复使用,而CountDownLatch一旦计数归零便无法重置。
[此处为图片2]
CopyOnWriteArrayList是一种适用于读多写少场景的线程安全集合。其工作原理是在修改操作(如添加、删除)时,先复制一份新的底层数组,在新数组上完成修改后,再将引用指向新数组。这种策略保证了读操作无需加锁,因此读取效率极高。
相比之下,Vector是一个较早的线程安全容器,其大多数方法都使用synchronized关键字进行同步,虽然保证了线程安全,但在高并发环境下容易成为性能瓶颈,因为每次访问都要竞争同一把锁。
LockSupport是构建Java同步组件的基础工具类,位于java.util.concurrent.locks包中。它提供了低级别的线程阻塞与唤醒能力,核心方法为park()和unpark()。
park()方法会使当前线程被阻塞,直到其他线程调用unpark()将其唤醒。与传统的wait()/notify()机制不同,LockSupport不需要依赖对象监视器,也不必持有锁,更加灵活且不易出错,常被用于AQS(AbstractQueuedSynchronizer)等高级同步器的实现中。
StampedLock是Java 8新增的一种高性能读写锁,相较于传统的ReadWriteLock,它引入了三种模式:写锁、悲观读锁和乐观读锁。
其中最关键的区别在于支持“乐观读”机制。在某些读操作中,可以先尝试获取乐观读锁,若在此期间没有发生写操作,则无需升级为悲观读锁,从而减少锁竞争,提高吞吐量。而ReadWriteLock仅支持标准的读锁和写锁,不具备此优化能力。
在Java多线程编程中,线程饥饿指的是某个线程由于长期无法获取必要的资源(如CPU时间片、锁等),导致其任务迟迟无法执行的现象。这种情况通常出现在优先级调度不当或锁被持续占用的情况下。
为防止线程饥饿,可以采用公平锁来确保等待最久的线程优先获得锁,合理设置线程优先级,避免个别线程长时间占用共享资源,并及时释放已持有的锁或其他临界资源。
Thread.yield()是Java中的一个静态方法,作用是向线程调度器发出提示:当前线程愿意暂时让出CPU执行权,以便其他相同或更高优先级的线程有机会运行。
需要注意的是,yield()只是一个建议,并不保证一定会发生线程切换,具体行为取决于JVM的调度策略。因此,不应将其作为控制程序正确性的手段,而更多用于调试或性能微调场景。
synchronized是Java中用于实现线程同步的关键字,可用于修饰实例方法、静态方法或代码块。当线程进入被synchronized修饰的区域时,必须先获取对应的对象监视器锁(也称内部锁),执行完毕后自动释放。
在任一时刻,只有一个线程能持有该锁,其他试图进入同步区域的线程将被阻塞,直到锁被释放。这种机制有效防止了多个线程同时访问共享资源而导致的数据不一致问题。
Java中的Semaphore是一个计数信号量,用于控制可以同时访问某一资源的线程数量。它通过维护一个许可集来实现对资源并发访问的限制。
主要用途包括:
- 限制并发执行的线程数目,例如控制对有限带宽或处理能力的服务调用;
- 实现资源池机制,如数据库连接池、线程池等,确保不会超出系统承载能力。
在Java并发编程中,LockSupport类提供了一种更为底层和灵活的线程阻塞与唤醒机制。
其核心特性如下:
- 作为线程阻塞工具,LockSupport不需要依赖synchronized块即可挂起或恢复线程;
- 主要方法为park()和unpark(Thread thread),分别用于暂停当前线程和唤醒指定线程;
- 该机制不依赖传统的锁或条件变量,因此在复杂的同步场景下提供了更高的灵活性。
关于Future与CompletableFuture的区别:
- Future是早期Java版本提供的接口,代表一个异步计算的结果,但功能较为基础,仅支持获取结果、取消任务以及查询是否完成;
- CompletableFuture则是Java 8引入的重要增强,不仅实现了Future接口,还实现了CompletionStage接口;
- 它支持方法链式调用和组合式异步编程,例如使用thenApply、thenCombine、exceptionally等方法进行结果转换与异常处理,极大提升了异步编程的表达能力和可维护性。
正确使用wait和notify机制需要注意以下几点:
- 必须在循环中调用wait(),防止因虚假唤醒导致程序错误;
- wait和notify必须在synchronized修饰的代码块或方法中执行,以确保线程已获得对象锁;
- 应确保操作的是正确的对象监视器,否则会抛出IllegalMonitorStateException;
- 通常建议优先使用notifyAll()而非notify(),以唤醒所有等待线程,避免某些线程永远无法被激活而导致死锁风险。
CountDownLatch是一种高效的同步辅助工具,适用于一个或多个线程等待其他线程完成一系列操作后再继续执行的场景。
使用方式如下:
- 创建CountDownLatch实例时传入一个整型计数器,表示需要等待的操作数量;
- 在等待线程中调用await()方法阻塞自身,直到计数归零;
- 每一个完成任务的线程调用countDown()方法将计数器减一;
- 当计数器变为0时,所有被await阻塞的线程将被释放并继续执行。
关于锁的升级与降级:
- StampedLock支持锁的降级和升级,允许从读锁升级为写锁(需满足条件)或从写锁安全降级为读锁;
- 而传统的ReadWriteLock并不支持此类操作,尤其不能进行锁升级,否则容易引发死锁;
- 这种灵活性使得StampedLock在高并发读取且偶尔写入的场景中表现更优。
在线程并发环境中,线程饥饿和线程活锁是两种常见的问题:
- 线程饥饿:指某个线程长期得不到所需的CPU时间或资源,通常由于线程优先级设置不合理,或某些线程长时间持有共享资源所致;
- 线程活锁:表现为线程虽然没有阻塞,但在重复尝试某项操作时始终失败,因为其他线程也在进行相同的操作,彼此干扰;
- 应对策略包括合理调整线程优先级、采用公平锁机制、避免长时间占用锁资源,以及为重试逻辑添加随机延迟或指数退避机制,从而减少冲突概率。


雷达卡


京公网安备 11010802022788号







