2026/2/16 22:09:13
网站建设
项目流程
网站建设 报告,wordpress仿微博发文插件,今天安阳发生的重大新闻,域名权重查询概述
按实现层面划分 [内置锁]#xff1a;synchronized是JVM层面实现的#xff0c;无需手动释放锁#xff0c;属于内置锁。[显式锁]#xff1a;ReentrantLock为代表的显式锁#xff0c;需要手动释放锁#xff0c;功能更加灵活#xff0c;位于java.util.concurrent.lock…概述按实现层面划分[内置锁]synchronized是JVM层面实现的无需手动释放锁属于内置锁。[显式锁]ReentrantLock为代表的显式锁需要手动释放锁功能更加灵活位于java.util.concurrent.locks包Java代码层面实现。按锁的竞争机制划分[悲观锁]synchronized和ReentrantLock都为悲观锁每次操作资源都会加锁阻塞其他线程。[乐观锁]不加锁直接操作通过CAS验证操作是否成功失败则重试例如AtomicInteger。按锁的可重入性划分[可重入锁]对于已获取到锁的线程可再次获取同一把锁避免自身死锁synchronized和ReentrantLock均为可重入锁。[不可重入锁]持有锁的线程无法再次获取这把锁容易引起自身死锁。锁的升级偏向锁 - 轻量级锁 - 重量级锁JDK1.6优化后synchronized会根据竞争激烈程度自动升级逐步提升并发性能具体一、synchronized1. 用法public class SynchronizedDemo { // 1. 修饰实例方法锁的是「当前类的实例对象」this public synchronized void instanceMethod() { // 线程安全的实例方法逻辑 System.out.println(修饰实例方法锁 this); } // 2. 修饰静态方法锁的是「当前类的 Class 对象」SynchronizedDemo.class public static synchronized void staticMethod() { // 线程安全的静态方法逻辑 System.out.println(修饰静态方法锁 SynchronizedDemo.class); } // 3. 修饰同步代码块锁的是「括号内指定的对象」灵活可控 public void codeBlockMethod() { // 可选锁对象this实例对象、Class 对象、自定义任意对象 Object lock new Object(); synchronized (lock) { // 线程安全的代码块逻辑 System.out.println(修饰代码块锁 lock); } } }⭐synchronized竞争的是一个资源对象无论是修饰的实例方法、静态方法还是代码块只是对象的类型不同而已。可以是方法当前类的实例对象、class对象、自定义的任意对象2. 实现原理依赖于「Java对象头」和「监视器锁」不同JDK版本略有差异JDK1.6之前仅支持「重量级锁」依赖操作系统的「互斥量Mutex」实现线程竞争时会触发「用户态 - 内核态」的上下文切换开销极大性能较差。JDK1.6之后引入了「锁升级」机制偏向锁 - 轻量级锁 - 重量级锁根据竞争激烈程度逐步升级大幅优化性能偏向锁无多线程竞争时锁偏向第一个获取锁的线程后续该线程再次获取锁时无需任何竞争操作直接获取锁开销极低。轻量级锁出现少量线程竞争时偏向锁升级为轻量级锁通过「CAS 操作」实现锁的获取与释放无需阻塞线程仅存在少量自旋开销。重量级锁出现大量线程竞争时轻量级锁升级为重量级锁依赖操作系统互斥量实现竞争失败的线程会被阻塞挂起避免空耗 CPU此时开销最大但能保证高并发场景下的稳定性。⭐锁升级是单向的无法降级。3. 核心特性可重入性线程持有锁后可再次获取同一把锁如同步方法中调用另一同步方法避免自身死锁。隐式释放锁无需手动调用释放方法同步代码块 / 方法执行完毕、抛出异常时JVM 会自动释放锁降低资源泄露风险。非公平锁默认采用非公平锁策略线程获取锁时不遵守先到先得的顺序可能存在插队现象吞吐量更高公平锁会带来额外的排队开销。二、ReentrantLock1. 用法import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { // 1. 创建 ReentrantLock 实例默认非公平锁传入 true 为公平锁 private static final ReentrantLock lock new ReentrantLock(false); public void doTask() { // 2. 获取锁lock() 方法阻塞式获取 lock.lock(); try { // 3. 执行线程安全的业务逻辑 System.out.println(Thread.currentThread().getName() 已获取锁执行任务); } finally { // 4. 释放锁必须在 finally 中保证无论是否异常都能释放锁 lock.unlock(); System.out.println(Thread.currentThread().getName() 已释放锁); } } public static void main(String[] args) { ReentrantLockDemo demo new ReentrantLockDemo(); // 启动 2 个线程竞争锁 new Thread(demo::doTask, 线程1).start(); new Thread(demo::doTask, 线程2).start(); } }⭐finally释放锁是必须的防止try抛出异常锁无法释放会导致其他线程永久阻塞引发死锁。2. 核心功能扩展ReentrantLock提供了synchronized不具备的高级功能支持公平锁/非公平锁创建实例的时候可以设置采用公平锁还是非公平锁默认非公平锁false支持获取锁超时通过trylock(long timeout, TimeUnit unit)方法设置锁获取超时时间超时后自动放弃获取锁返回false避免长时间阻塞。支持可中断获取锁通过lockInterruptibly()方法获取锁的线程可被其他线程中断调用thread.interrupt()中断后抛出InterruptedException并释放锁灵活控制线程状态。提供锁状态查询如isLocked()判断锁是否被持有、isHeldByCurrentThread()判断当前线程是否持有该锁、getHoldCount()获取当前线程持有该锁的次数方便监控和调试。3. 原理ReentrantLock的底层基于AQSAbstractQueuedSynchronizer抽象队列同步器实现AQS 是 JUC 包中所有显式锁、同步工具类的核心框架。 核心逻辑AQS 内部维护一个「volatile 状态变量state」和一个「双向链表同步队列」。state用于标记锁的状态state0表示锁未被持有state0表示锁被持有state的值等于线程持有锁的次数体现可重入性。线程获取锁时通过 CAS 操作修改state若state0则将state改为 1获取成功若state0且是当前线程持有则state加 1可重入否则加入同步队列阻塞等待。线程释放锁时将state减 1当state0时释放锁并唤醒同步队列中的下一个线程。4. 核心特性可重入性和synchronized一样支持线程重复获取同一把锁通过state变量计数实现。显式释放锁必须手动调用unlock()释放依赖开发者规范灵活性高但风险也高容易遗漏释放。功能灵活支持公平锁、超时获取、可中断等高级功能适配复杂并发场景。性能优异在高并发场景下性能略优于synchronizedJDK 1.6 后synchronized优化两者性能差距不大。三、synchronized锁升级锁的载体 --Java对象头synchronized锁的是「对象」而锁状态的信息就存储在Java 对象头Object Header中仅针对普通对象数组对象的对象头额外包含数组长度。 Java 对象头在 32 位 JVM 和 64 位 JVM 中长度分别为 8 字节和 16 字节核心包含两部分以 64 位 JVM 为例Mark Word标记字段8字节存储对象的锁状态、哈希码、GC年龄、偏向线程ID等核心信息是锁升级的「核心载体」。Klass Pointer类型指针8字节指向对象所属类的Class对象用于确定对象的类型。锁状态Mark Word 核心字段64 位无锁哈希码25 位 GC 年龄4 位 无锁标记1 位偏向锁偏向线程 ID54 位 偏向时间戳2 位 GC 年龄4 位 偏向锁标记1 位轻量级锁指向栈中锁记录的指针63 位 轻量级锁标记1 位重量级锁指向监视器锁Monitor的指针63 位 重量级锁标记1 位第一步无锁 - 偏向锁无竞争场景第一个线程获取锁时JVM 通过 CAS 操作将Mark Word中的「偏向线程 ID」设置为当前线程的 ID同时将「锁标记位」改为「偏向锁标记」。该线程后续再次获取该对象的锁时无需再执行 CAS 操作或阻塞只需简单检查Mark Word中的偏向线程 ID 是否为当前线程 ID偏向锁标记是否有效。若两者都满足直接获取锁成功「偏向」的含义就是锁会「偏爱」这个线程全程无额外开销效率极高。第二步偏向锁 → 轻量级锁少量线程竞争无阻塞加锁流程线程获取锁时先在自己的栈帧中创建一个「锁记录Lock Record」用于存储对象Mark Word的副本称为「Displaced Mark Word」。线程通过 CAS 操作将对象Mark Word中的内容替换为「指向当前线程栈中锁记录的指针」。若 CAS 成功说明锁获取成功将Mark Word的锁标记位改为「轻量级锁标记」线程继续执行同步代码。若 CAS 失败说明有其他线程正在竞争该锁当前线程不会被阻塞而是进入「自旋」反复执行 CAS 操作尝试获取锁。解锁流程线程执行完同步代码后通过 CAS 操作将对象Mark Word中的「锁记录指针」替换回原来的「Displaced Mark Word」栈中锁记录存储的副本。若 CAS 成功说明无其他线程竞争解锁成功锁状态回到无锁。若 CAS 失败说明有其他线程在自旋竞争此时会将轻量级锁升级为重量级锁同时唤醒自旋的线程。第三步轻量级锁 → 重量级锁大量线程竞争阻塞式竞争加锁流程锁升级为重量级锁后对象Mark Word中会存储「指向 Monitor 的指针」Monitor 是一个用于管理锁竞争的核心数据结构。线程获取锁时会尝试获取 Monitor 的「持有权」Monitor 中的owner字段记录持有锁的线程。若获取成功owner字段设置为当前线程线程执行同步代码。若获取失败当前线程会被加入 Monitor 的「阻塞队列」被操作系统挂起从用户态切换到内核态放弃 CPU 执行权直到被唤醒。解锁流程线程执行完同步代码后释放 Monitor 的持有权将owner字段置为 null。唤醒阻塞队列中的一个线程通过操作系统的唤醒机制让其尝试获取锁。锁状态保持为重量级锁不会降级单向升级。