Spring Bean 生命周期
简单来说,Spring Bean的生命周期可以分为四个大的阶段:实例化 (Instantiation) -> 属性赋值 (Population) -> 初始化 (Initialization) -> 销毁 (Destruction)。 下面我将详细拆解每个步骤,并提供一个流程图帮助你更好地理解。 核心生命周期流程图flowchart TD A[开始创建Bean] --> B[实例化 Instantiation] B --> C[属性赋值 Population] C --> D[初始化 Initialization] D --> E[投入使用] E --> F[销毁 Destruction] subgraph B [实例化] B1[推断构造函数] B2[执行构造函数 new] end subgraph C [属性赋值] C1[依赖注入 DI] C2[处理 Aware 接口] end subgr...
Spring容器启动流程
Spring容器的启动流程可以看作是一个精密的装配流水线,其核心目标是根据用户提供的配置元数据(Configuration Metadata),创建、配置并组装应用程序中的对象(Bean),最终形成一个完整的、可用的应用上下文(ApplicationContext)。 下面我将从宏观流程和核心步骤两个层面,并结合关键的源码接口,来详细解释Spring容器的启动流程。 宏观流程概述一个典型的Spring容器(以ClassPathXmlApplicationContext为例)启动流程可以概括为以下三个大阶段: 容器初始化阶段:为启动做准备,设置环境变量、资源解析器等。 Bean加载与解析阶段(核心中的核心):读取配置元数据,将其解析为容器内部数据结构(BeanDefinition)。 Bean实例化与依赖注入阶段:根据BeanDefinition的信息,通过反射创建Bean实例,并解决其依赖关系。 后处理与完成阶段:执行相关的后处理器,触发生命周期回调,发布上下文就绪事件。 为了更直观地理解,下图描绘了这个过程的核心步骤与迭代循环: flowchart TD A[Sp...
AbstractQueuedSynchronizer详解
AQSAQS(AbstractQueuedSynchronizer,抽象队列同步器)是JUC(java.util.concurrent)包中构建锁和其他同步组件(如Semaphore, CountDownLatch, ReentrantReadWriteLock等)的基础框架。 你可以把它理解成一个多线程访问共享资源的管理员或交警。它的核心职责是:当共享资源被占用时,将未能成功获取资源的线程阻塞,并将其放入一个队列中进行管理;当资源被释放时,再从队列中唤醒等待的线程来尝试获取。 AQS的核心思想可以概括为三点: 一个状态量(State): 这是一个volatile int类型的变量,表示共享资源的状态。 不同的同步器对它的含义解读不同。对于ReentrantLock,state表示锁的重入次数(0表示未被占用,>0表示被占用且数值为重入次数);对于Semaphore,state表示剩余的许可证数量;对于CountDownLatch,state表示倒计数的数值。 一个FIFO线程等待队列(CLH变体): 这是一个双向链表结构的队列,用于存放所有等待获取资源的线程。当...
synchronized和ReentrantLock的区别
在 Java 并发编程中,synchronized 和 ReentrantLock 都是实现线程同步、保证互斥访问共享资源的关键机制,但它们在设计理念、功能和使用方式上存在显著差异。以下是主要区别: 本质与实现方式 synchronized: Java 语言内置的关键字。 是 Java 语法的一部分。 隐式锁(监视器锁)。 基于 JVM 底层(对象头中的 Mark Word)的 monitorenter 和 monitorexit 指令实现锁的获取和释放。锁的获取和释放由 JVM 自动管理。 与对象关联。 锁是附加在对象上的(实例对象或 Class 对象)。 ReentrantLock: java.util.concurrent.locks 包中的一个类。 是 JDK 提供的 API。 显式锁。 开发者需要显式地调用 lock() 方法来获取锁,并(必须)在 finally 块中调用 unlock() 方法来释放锁。 独立的对象。 ReentrantLock 本身就是一个锁对象。 锁的获取与释放 synchronized: 自动获取与释放。 线程进入 synchroni...
synchronized的偏向锁、轻量级锁、重量级锁
Java 中的 synchronized 关键字是实现线程同步的核心机制。为了在不同竞争程度的场景下优化性能,JVM 采用了锁升级策略。对象锁的状态会随着竞争情况的变化,从无锁状态逐步升级:偏向锁 -> 轻量级锁 -> 重量级锁。 这些锁的状态信息存储在 Java 对象头的 Mark Word 中。 偏向锁 (Biased Locking) 目标: 优化同一个线程多次获取同一把锁的场景(无实际竞争或极少竞争)。 原理: 当一个线程第一次获得锁时,JVM 会在对象头的 Mark Word 中记录该线程的 ID(偏向线程ID)以及一个偏向时间戳(epoch)。 之后该线程再次进入这个同步块时,不需要进行任何同步操作(如 CAS、系统调用),只需简单检查 Mark Word 中的线程 ID 是否指向自己: 如果是:直接执行。 如果不是:说明存在竞争(即使另一个线程已经释放了锁,偏向也不会自动撤销),此时需要撤销偏向锁。 撤销偏向锁: 需要等到全局安全点(此时 JVM 暂停所有工作线程)。 检查持有偏向锁的线程是否还活着或是否仍在同步块内。 如果线程已不存活或不在同...
Java线程池中任务未捕捉异常问题分析
当一个在 Java 线程池中执行的任务抛出了未捕获的异常时,会导致以下几个关键问题: 任务执行线程的终止(最直接的影响)默认情况下,未捕获的异常会导致执行该任务的当前线程结束。 对于 ThreadPoolExecutor:线程池中的核心线程(corePoolSize)是宝贵的资源。如果一个核心线程因为任务异常而终止,线程池为了维持核心线程数,会创建一个新的线程来替代它。虽然线程池有自我修复能力,但这个创建和销毁的过程会有额外的开销。 对于 ForkJoinPool:行为类似,工作线程可能会被终止并可能被替换。 关键点:任务本身的失败不会直接影响线程池中其他线程的执行,但会导致执行它的那个特定线程结束。 异常信息的丢失(非常危险!)这是最隐蔽和危险的问题。默认情况下,这个未捕获的异常只会被打印到标准错误流(System.err),而通常没有人会一直盯着控制台日志。 123456ExecutorService executor = Executors.newFixedThreadPool(2);executor.execute(() -> { throw n...
Java线程池动态调整队列容量
解决线程池中BlockingQueue无法动态调整大小的问题在Java线程池中,标准BlockingQueue实现(如ArrayBlockingQueue)创建后容量固定,无法动态调整。以下是完整的解决方案: 问题核心痛点 固定容量缺陷: 低负载时:队列过大浪费内存 高负载时:队列快速填满触发拒绝策略 业务场景需求: 流量波动大的系统(如电商秒杀) 云环境弹性伸缩(K8s HPA) 成本敏感型应用(按需分配资源) 解决方案:动态可调整队列方案1:自定义可调整阻塞队列1234567891011121314151617181920212223242526272829303132333435363738394041public class ResizableBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> { private final ReentrantLock lock = new ReentrantLock(); pri...
Java线程池为什么优先入队而非直接创建最大线程
Java线程池采用核心线程→入队→非核心线程的任务处理流程,这种设计背后有深刻的架构考量。 核心设计目标 资源节约:避免不必要的线程创建(线程创建成本≈1ms/次,消耗1MB+栈内存) 流量削峰:用队列缓冲突发流量,防止线程爆炸 稳定性优先:优先保证系统稳定,其次追求吞吐量 优先入队的本质原因 线程创建成本 远大于 入队成本 12345// 线程创建:需要系统调用、内存分配、上下文切换new Thread(() -> {...}).start(); // 成本高// 任务入队:只是内存操作queue.offer(task); // 成本极低 防止线程爆炸(Thread Thrashing) 如果直接创建最大线程: 突发1000个任务 → 立即创建1000个线程 系统资源可能被耗尽(内存/CPU上下文切换) 后续任务直接触发拒绝策略 优先入队方案: 突发1000任务–>核心线程处理N个–>队列缓冲M个–>非核心线程处理K个–>稳定后自动回收 符合典型业务场景 真实场景中: 70%的任务能在10...
Java线程池底层工作原理
Java线程池底层工作原理Java线程池的核心实现位于java.util.concurrent.ThreadPoolExecutor类,其底层设计围绕任务调度、资源复用和流量控制展开。以下是关键机制的分步解析: 核心参数与状态管理线程池通过一个32位的ctl原子变量同时管理线程池状态和工作线程数量: 12private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 高3位:线程池状态,低29位:有效线程数 状态变迁: RUNNING:接受新任务,处理队列任务 SHUTDOWN:不接受新任务,处理队列任务 STOP:不接受新任务,不处理队列,中断进行中任务 TIDYING:所有任务终止,workerCount=0 TERMINATED:terminated()钩子执行完毕 任务执行流程(execute()方法) 核心线程处理 当前线程数 < corePoolSize → 创建新Worker(立即执行任务) 1addWorker(command, true); /...
Java线程死锁
在Java中,死锁(Deadlock)是指两个或多个线程在执行过程中,因争夺资源而陷入互相等待的僵局。当以下四个必要条件同时满足时,就会发生死锁: 死锁产生的必要条件 互斥条件(Mutual Exclusion)资源一次只能被一个线程占用(例如:synchronized锁、数据库连接等独占资源)。 请求与保持(Hold and Wait)线程持有至少一个资源,同时请求其他线程占有的资源(不释放已持有资源)。 不可剥夺(No Preemption)线程已获得的资源在未使用完前,不能被强行剥夺(只能主动释放)。 循环等待(Circular Wait)存在线程资源的环形等待链:线程A → 等待 线程B 的资源 → 线程B → 等待 线程A 的资源。 死锁示例代码12345678910111213141516171819202122232425262728293031public class DeadlockDemo { private static final Object lock1 = new Object(); private static f...
