深入拆解Java线程:生命周期流转与核心方法底层原理
线程是Java并发编程的核心执行单元理解其生命周期与状态转换机制以及interrupt()、wait()、notify()、join()等核心方法的底层原理是编写高效、稳定并发程序的基础。一、Java线程的生命周期与状态转换Java线程的状态由java.lang.Thread.State枚举定义共包含6种状态这些状态在特定条件下会相互转换形成完整的生命周期流转。1.1 线程状态详解NEW新建线程对象已创建但尚未调用start()方法启动。此时线程仅在JVM内存中存在未分配CPU时间片也未开始执行。RUNNABLE可运行线程已调用start()方法启动处于可执行状态。该状态包含两种子状态Ready线程已准备好等待CPU时间片调度Running线程正在CPU上执行Java将这两种子状态统一称为RUNNABLE因为线程在Ready和Running之间的切换由JVM线程调度器控制应用程序无法直接感知。BLOCKED阻塞线程因等待监视器锁synchronized而被阻塞。当线程尝试进入被其他线程持有的synchronized块/方法时会进入该状态直到获取到锁才会转换回RUNNABLE。WAITING无限期等待线程进入无限期等待状态需等待其他线程执行特定操作如notify()/notifyAll()才能被唤醒。进入该状态的场景包括调用无超时参数的Object.wait()调用无超时参数的Thread.join()调用LockSupport.park()TIMED_WAITING有时限等待线程进入有时限的等待状态超过指定时间后会自动唤醒或在等待时间内被其他线程唤醒。进入该状态的场景包括调用Thread.sleep(long millis)调用带超时参数的Object.wait(long timeout)调用带超时参数的Thread.join(long millis)调用LockSupport.parkNanos(long nanos)调用LockSupport.parkUntil(long deadline)TERMINATED终止线程执行完毕生命周期结束。线程进入该状态的原因包括run()方法正常执行完毕线程执行过程中抛出未捕获的异常1.2 状态转换全流程二、核心方法的底层原理与正确使用场景2.1 interrupt()线程中断机制底层原理interrupt()的核心作用是设置线程的中断标志位而非强制终止线程。线程需要主动检查中断标志位并响应中断这是一种协作式的中断机制。相关方法说明void interrupt()设置线程的中断标志位为trueboolean isInterrupted()检查线程的中断标志位不清除标志位static boolean interrupted()检查当前线程的中断标志位清除标志位将其重置为false当线程处于WAITING或TIMED_WAITING状态时调用interrupt()会抛出InterruptedException并清除中断标志位因此在捕获该异常后通常需要重新设置中断标志位以便上层逻辑感知。正确使用场景interrupt()主要用于优雅地停止线程避免使用已废弃的stop()方法该方法会强制终止线程导致资源未释放、数据不一致等问题。代码实例package com.jam.demo; import lombok.extern.slf4j.Slf4j; Slf4j public class InterruptDemo implements Runnable { Override public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(1000); log.info(线程正在执行任务); } catch (InterruptedException e) { log.info(捕获到InterruptedException重置中断标志位); Thread.currentThread().interrupt(); } } log.info(线程响应中断结束运行); } public static void main(String[] args) throws InterruptedException { Thread thread new Thread(new InterruptDemo(), ken-interrupt-thread); thread.start(); Thread.sleep(3500); log.info(主线程调用interrupt()中断子线程); thread.interrupt(); } }2.2 wait()、notify()、notifyAll()线程协作底层原理这三个方法是java.lang.Object类的native方法需与synchronized关键字配合使用核心是基于对象的监视器Monitor实现线程间的等待/通知机制。Monitor结构每个对象都关联一个Monitor包含两个队列Entry Set入口集存放等待获取对象锁的线程对应BLOCKED状态Wait Set等待集存放调用wait()后进入等待的线程对应WAITING/TIMED_WAITING状态wait()原理释放当前持有的对象锁将当前线程加入Wait Set线程进入WAITING/TIMED_WAITING状态notify()原理从Wait Set中随机选择一个线程唤醒被唤醒的线程从Wait Set移动到Entry Set被唤醒的线程等待获取对象锁获取后进入RUNNABLE状态notifyAll()原理唤醒Wait Set中的所有线程所有被唤醒的线程从Wait Set移动到Entry Set这些线程竞争对象锁获取到锁的线程进入RUNNABLE状态注意事项必须在synchronized块/方法中调用否则会抛出IllegalMonitorStateException循环检查条件避免虚假唤醒Spurious Wakeups即线程可能在没有被notify()/notifyAll()的情况下自动唤醒优先使用notifyAll()notify()可能导致信号丢失notifyAll()能确保所有等待线程都有机会被唤醒正确使用场景主要用于实现线程间的协作如生产者-消费者模式、Future任务实现等。代码实例生产者-消费者模式package com.jam.demo; import lombok.extern.slf4j.Slf4j; import java.util.LinkedList; import java.util.Queue; Slf4j public class ProducerConsumerDemo { private static final int QUEUE_CAPACITY 5; private final QueueInteger queue new LinkedList(); public void produce() throws InterruptedException { synchronized (queue) { while (queue.size() QUEUE_CAPACITY) { log.info(队列已满生产者线程等待); queue.wait(); } int product (int) (Math.random() * 100); queue.offer(product); log.info(生产者生产产品{}当前队列大小{}, product, queue.size()); queue.notifyAll(); } } public void consume() throws InterruptedException { synchronized (queue) { while (queue.isEmpty()) { log.info(队列为空消费者线程等待); queue.wait(); } int product queue.poll(); log.info(消费者消费产品{}当前队列大小{}, product, queue.size()); queue.notifyAll(); } } public static void main(String[] args) { ProducerConsumerDemo demo new ProducerConsumerDemo(); Thread producer new Thread(() - { try { for (int i 0; i 10; i) { demo.produce(); Thread.sleep(400); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, ken-producer); Thread consumer1 new Thread(() - { try { for (int i 0; i 5; i) { demo.consume(); Thread.sleep(700); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, ken-consumer-1); Thread consumer2 new Thread(() - { try { for (int i 0; i 5; i) { demo.consume(); Thread.sleep(900); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, ken-consumer-2); producer.start(); consumer1.start(); consumer2.start(); } }2.3 join()线程等待底层原理join()的底层是通过调用wait()实现的让当前线程等待目标线程执行完毕。当目标线程执行完毕后JVM会自动调用notifyAll()唤醒所有等待的线程。核心逻辑简化版public final synchronized void join(long millis) throws InterruptedException { long base System.currentTimeMillis(); long now 0; if (millis 0) { throw new IllegalArgumentException(timeout value is negative); } if (millis 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay millis - now; if (delay 0) { break; } wait(delay); now System.currentTimeMillis() - base; } } }正确使用场景主要用于等待一个或多个线程执行完毕后再继续执行当前线程如并行任务的结果汇总。代码实例package com.jam.demo; import lombok.extern.slf4j.Slf4j; Slf4j public class JoinDemo { public static void main(String[] args) throws InterruptedException { Thread task1 new Thread(() - { try { log.info(任务1开始执行); Thread.sleep(2000); log.info(任务1执行完毕); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, ken-task-1); Thread task2 new Thread(() - { try { log.info(任务2开始执行); Thread.sleep(3000); log.info(任务2执行完毕); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }, ken-task-2); task1.start(); task2.start(); log.info(主线程等待任务1和任务2执行完毕); task1.join(); task2.join(); log.info(所有任务执行完毕主线程继续执行); } }三、易混淆技术点区分3.1 wait() vs sleep()特性wait()sleep()所属类ObjectThread锁释放释放当前持有的对象锁不释放锁使用场景线程间协作暂停当前线程执行唤醒条件需被notify()/notifyAll()唤醒或超时超时时间到或被interrupt()异常声明抛出InterruptedException抛出InterruptedException3.2 notify() vs notifyAll()notify()只唤醒Wait Set中的一个线程选择策略由JVM实现决定通常是随机或FIFOnotifyAll()唤醒Wait Set中的所有线程这些线程会竞争对象锁在大多数场景下建议使用notifyAll()避免信号丢失问题。3.3 isInterrupted() vs interrupted()isInterrupted()实例方法检查调用该方法的线程的中断标志位不清除标志位interrupted()静态方法检查当前线程的中断标志位清除标志位重置为false总结理解Java线程的生命周期与状态转换以及interrupt()、wait()、notify()、join()等核心方法的底层原理是编写高质量并发程序的关键。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2513268.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!