synchronized标记synchronized 的范围标记在 Java 中synchronized关键字可以用于标记代码块或方法以实现线程同步。范围标记分为两种形式同步代码块通过指定一个对象作为锁仅同步代码块内的逻辑。语法如下synchronized (lockObject) { // 需要同步的代码 }lockObject可以是任意对象实例通常使用共享资源或专用锁对象。同步方法直接修饰方法锁对象为当前实例非静态方法或类的 Class 对象静态方法。语法如下public synchronized void method() { ... } // 实例锁 public static synchronized void staticMethod() { ... } // 类锁synchronized 的对象标记synchronized的行为依赖于锁对象的选择不同锁对象影响同步范围实例对象锁非静态同步方法或代码块使用this作为锁时锁的是当前对象实例。同一实例的多个同步方法会互斥。类对象锁静态同步方法或代码块使用Class对象如MyClass.class时锁的是整个类所有实例的同步静态方法会互斥。自定义对象锁通过显式指定其他对象如私有成员变量作为锁可以实现更细粒度的控制。例如private final Object lock new Object(); public void method() { synchronized (lock) { ... } }关键注意事项锁对象的唯一性必须确保多个线程使用的是同一个锁对象才能实现同步。避免锁泄露不要使用可能被外部修改的对象如公共变量作为锁。性能影响过度使用synchronized可能导致线程阻塞降低并发性能。通过合理选择锁对象和同步范围可以平衡线程安全与性能需求。synchronized锁的升级过程Java中的synchronized锁会根据竞争情况从偏向锁升级到轻量级锁最终升级到重量级锁。这种锁升级机制是为了在无竞争和多线程竞争环境下平衡性能。偏向锁偏向锁适用于只有一个线程访问同步块的场景。当线程第一次进入同步块时会在对象头和栈帧中的锁记录中存储偏向的线程ID。后续该线程进入同步块时只需检查对象头的Mark Word是否指向当前线程ID。偏向锁的获取过程检查对象头的Mark Word是否存储当前线程ID。如果是直接进入同步块。如果不是尝试通过CAS操作替换Mark Word中的线程ID。成功则获取锁失败则升级为轻量级锁。偏向锁的释放不会主动发生只有当其他线程尝试获取锁时才会释放。轻量级锁当多个线程交替访问同步块但不存在竞争时锁会升级为轻量级锁。轻量级锁通过CAS操作和自旋来实现同步。轻量级锁的获取过程在栈帧中创建锁记录空间Lock Record。将对象头的Mark Word复制到锁记录中Displaced Mark Word。尝试通过CAS将对象头的Mark Word替换为指向锁记录的指针。成功则获取锁失败则自旋重试或升级为重量级锁。轻量级锁的释放过程使用CAS操作将Displaced Mark Word替换回对象头。成功则释放完成失败则说明有竞争锁已膨胀为重量级锁。重量级锁当多个线程同时竞争同一锁时轻量级锁会升级为重量级锁。重量级锁依赖于操作系统提供的互斥量mutex实现涉及线程的阻塞和唤醒开销较大。重量级锁的特点线程竞争锁失败后会进入阻塞状态。锁释放时会唤醒等待线程。涉及用户态和内核态的切换性能开销较大。适用于高并发竞争场景。锁升级的条件偏向锁升级为轻量级锁当有第二个线程尝试获取锁时。轻量级锁升级为重量级锁当自旋次数超过阈值默认10次或等待线程数超过1个。重量级锁不会降级除非发生GC因为GC会清除所有锁状态。性能比较偏向锁加锁和解锁不需要额外消耗适合单线程场景。轻量级锁线程不会阻塞通过自旋消耗CPU时间适合低竞争场景。重量级锁线程会阻塞避免CPU空转适合高竞争场景。Java对象头结构Java对象头是JVM中每个对象实例的元数据部分存储了对象的运行时信息。对象头通常包含以下两部分Mark Word存储对象自身的运行时数据如哈希码、GC分代年龄、锁状态标志等。在32位JVM中占32位64位JVM中占64位开启压缩指针时为32位。Klass Pointer指向对象所属类的元数据的指针。32位JVM中占32位64位JVM中占64位开启压缩指针时为32位。Mark Word的详细内容Mark Word的内容会根据对象状态动态变化以下是HotSpot虚拟机中的典型布局以64位系统为例无锁状态| 25bit哈希码 | 4bit分代年龄 | 1bit偏向锁标志0 | 2bit锁标志01 |偏向锁状态| 23bit线程ID | 2bit Epoch | 4bit分代年龄 | 1bit偏向锁标志1 | 2bit锁标志01 |轻量级锁状态| 62bit指向栈中锁记录的指针 | 2bit锁标志00 |重量级锁状态| 62bit指向互斥量的指针 | 2bit锁标志10 |GC标记状态| 空 | 2bit锁标志11 |对象头大小计算32位JVM对象头总大小 8字节Mark Word 4字节 Klass Pointer 4字节64位JVM未开启压缩指针对象头总大小 16字节Mark Word 8字节 Klass Pointer 8字节64位JVM开启压缩指针对象头总大小 12字节Mark Word 8字节 Klass Pointer 4字节查看对象头的方法使用JOLJava Object Layout工具可以分析对象头// 添加Maven依赖 dependency groupIdorg.openjdk.jol/groupId artifactIdjol-core/artifactId version0.16/version /dependency // 示例代码 System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());输出示例java.lang.Object object internals: OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0) 8 4 (object header: class) 0xf80001e5 12 4 (object alignment gap) Instance size: 16 bytes对象头与锁升级对象头中的Mark Word会随着锁竞争升级而变化无锁初始状态未发生同步。偏向锁当同一线程多次访问时Mark Word记录线程ID。轻量级锁发生竞争时Mark Word转换为指向线程栈中锁记录的指针。重量级锁竞争加剧时Mark Word指向互斥量Monitor。注意事项数组对象会在对象头中额外存储数组长度4字节。对象头布局是JVM实现相关的不同虚拟机可能有差异。压缩指针-XX:UseCompressedOops可减少64位系统中的内存占用。原子操作原子操作的定义原子操作指在多线程或并发环境中不可分割的操作执行过程中不会被其他线程中断保证操作的完整性和一致性。原子操作是并发编程的基础常用于实现锁、计数器等同步机制。原子操作的特性不可分割性操作要么完全执行要么完全不执行不会出现中间状态。可见性操作完成后结果对其他线程立即可见。有序性操作不会被编译器或处理器重排序破坏逻辑。原子操作的实现方式硬件支持的原子指令现代处理器提供原子指令如x86的LOCK前缀、ARM的LDREX/STREX直接通过CPU指令实现原子操作。例如CASCompare-And-Swap比较并交换若当前值等于预期值则更新为新值。bool atomic_compare_exchange(int* ptr, int expected, int new_val) { return __sync_val_compare_and_swap(ptr, expected, new_val); }编程语言提供的原子类型C11、Java等语言内置原子类型如std::atomic、AtomicInteger封装底层硬件指令#include atomic std::atomicint counter(0); counter.fetch_add(1); // 原子递增操作系统提供的原子API如Linux的atomic_t类型或Windows的Interlocked系列函数// Windows示例 LONG InterlockedIncrement(LONG volatile* Addend);常见原子操作场景计数器无锁统计避免多线程竞争。标志位控制如线程安全的开关状态切换。无锁数据结构如无锁队列、栈的实现。原子操作的局限性性能开销原子指令可能比普通操作慢频繁使用会影响性能。ABA问题CAS操作中值从A变为B又变回A可能导致误判。解决方案如版本号标记。示例无锁队列的原子操作templatetypename T class LockFreeQueue { std::atomicNode* head; std::atomicNode* tail; public: void push(T val) { Node* new_node new Node(val); Node* old_tail tail.exchange(new_node); old_tail-next new_node; } };通过合理使用原子操作可以高效解决并发问题但需结合场景权衡性能与复杂度。