线程中断是Java中一种优雅的线程协作机制,其核心设计理念是"请求而非强制"。与早期被废弃的Thread.stop()方法不同,中断机制给予被中断线程充分的自主权,让其能够安全地完成资源清理工作后再终止。这种设计有效避免了强制终止可能导致的资源泄漏、数据不一致等问题。
在操作系统层面,中断通常指硬件或软件发出的信号,要求处理器暂停当前任务处理紧急事件。Java将这一概念抽象到线程层面,但采用了更温和的实现方式。理解这一点对正确使用中断机制至关重要——中断不是命令,而是协商。
java复制public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 设置中断标志
b.interrupt(this);
return;
}
}
interrupt0();
}
这个方法的核心作用是将线程的中断状态设置为true。关键点在于:
java复制public boolean isInterrupted() {
return isInterrupted(false);
}
这个非静态方法的特点是:
java复制public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
这个静态方法的特殊之处:
正确的线程中断处理通常遵循以下模式:
java复制public void run() {
while (!Thread.interrupted()) {
try {
// 正常业务逻辑
doWork();
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
// 执行清理工作
cleanUp();
break;
}
}
}
关键注意事项:
在使用线程池时,中断处理有特殊考量:
java复制ExecutorService executor = Executors.newFixedThreadPool(4);
Future<?> future = executor.submit(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 任务逻辑
}
});
// 取消任务
future.cancel(true); // true表示尝试中断任务
重要细节:
LockSupport是Java并发包中的基石类,提供线程阻塞和唤醒的底层能力。与synchronized和Lock接口相比,它具有以下特点:
| 特性 | LockSupport | synchronized | ReentrantLock |
|---|---|---|---|
| 实现级别 | 最底层 | 语言级 | API级 |
| 许可机制 | 是 | 否 | 间接使用 |
| 精确唤醒 | 单线程 | 随机/全部 | 条件队列 |
| 死锁风险 | 无 | 有 | 有 |
| 性能开销 | 最低 | 中等 | 较高 |
LockSupport的核心是许可证机制:
关键代码实现:
java复制public static void park() {
UNSAFE.park(false, 0L);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
java复制class MyLock {
private volatile Thread owner;
private volatile int count;
public void lock() {
Thread current = Thread.currentThread();
if (owner == current) {
count++;
return;
}
while (!compareAndSetOwner(null, current)) {
LockSupport.park(this);
}
}
public void unlock() {
if (owner != Thread.currentThread())
throw new IllegalMonitorStateException();
if (--count == 0) {
owner = null;
LockSupport.unpark(nextWaiter);
}
}
}
java复制Thread worker = new Thread(() -> {
while (true) {
LockSupport.park();
doWork();
}
});
// 主线程精确唤醒工作线程
LockSupport.unpark(worker);
现代计算机的多级缓存架构:
code复制CPU核心1 ──> L1缓存 ──> L2缓存 ──> L3缓存 ──> 主内存
CPU核心2 ──> L1缓存 ──> L2缓存 ──> L3缓存 ──> 主内存
JMM的抽象模型:
code复制线程A ──> 工作内存A ──> 主内存
线程B ──> 工作内存B ──> 主内存
关键差异:
Java中的四种内存屏障:
| 屏障类型 | 作用 | 对应Java操作 |
|---|---|---|
| LoadLoad | 禁止读-读重排序 | volatile读 |
| StoreStore | 禁止写-写重排序 | volatile写 |
| LoadStore | 禁止读-写重排序 | volatile读 |
| StoreLoad | 禁止写-读重排序(全能屏障) | volatile写 |
典型volatile变量写操作的内存语义:
单线程中的操作按程序顺序执行,但允许编译器优化重排序,只要不影响执行结果。
java复制synchronized (obj) {
x = 1; // 操作A
}
// 释放锁
synchronized (obj) {
// 操作B一定能看到x=1
}
java复制volatile boolean flag = false;
// 线程1
x = 1; // 操作A
flag = true; // 操作B
// 线程2
if (flag) { // 操作C
// 这里能看到x=1 操作D
}
操作A happens-before 操作B,操作C happens-before 操作D
java复制class SafePublication {
static volatile Resource resource;
public static void init() {
Resource temp = new Resource(); // 操作A
resource = temp; // 操作B
}
public static void use() {
if (resource != null) { // 操作C
resource.doSomething(); // 操作D
}
}
}
happens-before关系链:
A → B (程序顺序规则)
B → C (volatile规则)
C → D (程序顺序规则)
通过传递性:A → D
误区1:认为happens-before是时间先后关系
误区2:忽视final字段的特殊规则
误区3:过度依赖happens-before而忽略实际需求
java复制@Immutable
public final class Product {
private final String id;
private final BigDecimal price;
public Product(String id, BigDecimal price) {
this.id = id;
this.price = price;
}
// 只有getter方法
}
java复制ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
| 场景 | 推荐容器 | 特性说明 |
|---|---|---|
| 高频读少写 | CopyOnWriteArrayList | 写时复制,读无锁 |
| 高并发Map | ConcurrentHashMap | 分段锁/Node锁,高并发 |
| 优先级队列 | PriorityBlockingQueue | 线程安全的优先级队列 |
| 延迟任务 | DelayQueue | 基于时间的调度 |
问题1:中断状态被意外清除
问题2:不可中断的阻塞IO
问题1:失效数据
问题2:指令重排序
诊断工具:
bash复制jstack <pid> # 查看线程栈
jconsole # 图形化监控
预防措施:
在实际开发中,理解这些底层机制能帮助我们写出更高效、更可靠的并发程序。建议结合JVM参数-XX:+PrintAssembly查看汇编代码,深入理解内存屏障的实际效果。对于性能关键路径,应当进行基准测试(JMH)而非猜测。