Java并发包中锁机制的底层实现原理剖析
实现java并发包中的锁机制底层主要有两种方式1.基于jvm的monitor机制和对象头中的marksynchronized关键字 word实现并通过锁升级(偏向锁→轻量级锁→重量级锁)优化性能2.java.util.concurrent.locks包中的锁基于abstractquedsynchronizeraqs通过volatile实现 state变量、cas操作和clh队列管理线程同步。在字节码层面synchronized依赖于monitorenter/monitorexit指令控制锁的获取和释放而aqs则通过子类重写tryacquire/tryrelease来定义同步语义并使用locksupport来阻塞和唤醒线程。此外并发包还包括atomic系列(基于cas实现无锁操作)、countdownlatch(基于aqs实现计数等待)、semaphore(基于aqs实现资源许可控制)、reentrantreadwritelock(基于aqs拆分state实现读写分离)和cyclicbarier(基于reentrantlock和condition实现循环屏障)等重要同步工具共同构成了java并发编程的核心支持系统。Java并发包中锁机制的底层实现原理剖析Java并发包中的锁机制其底层实现原理主要围绕两个核心点展开:一个是基于对象头和操作系统相互排斥JVM层面的synchronized关键词另一个是java.util.concurrent.在locks包中AbstractQuedsynchronizerAQS为基础构建的各种锁和同步器。了解它们的关键是掌握JVM对线程状态的管理调用操作系统的原始语言以及如何通过CASCompare-And-Swap操作以确保原子性和可见性。在我看来虽然这两种机制有不同的路径但它们都以不同的方式解决了并发场景下的资源竞争问题但synchronized更像是JVM的“黑盒魔法”而AQS提供了一种更灵活、更可控的“积木构建”方法。Java并发包中锁机制的底层实现原理剖析解决方案要深入分析Java并发包中锁机制的底层实现必须从两个主要分支入手:synchronized关键词和Java。.U.Cjava.util.concurrent包下的锁。synchronized关键词synchronized是Java语言层面的同步原语可作用于方法实例方法和静态方法或代码块。它的底层实现取决于JVM的Monitor管程机制。当一个线程试图获得synchronized锁时JVM将试图在对象的对象头上进行测试Object Header操作在中间。Mark在对象头中 Word字段是其核心它记录了对象的哈希码、GC信息和锁定状态。立即学习“Java免费学习笔记(深入)Java并发包中锁机制的底层实现原理剖析为了优化synchronized的性能JVM引入了锁升级Lock Escalation机制偏向锁Biased Locking 当一个线程首次访问同步块时如果对象没有被其他线程锁定JVM将对象的Mark Word设置为偏向模式并记录当前线程的ID。当后续线程再次进入同步块时只需检查Mark即可 无论Word中的线程ID是否为自己性能成本都非常低。轻量级锁Lightweight Locking 当第二个线程试图获得相同的synchronized锁时偏向锁将升级为轻量级锁。此时JVM将在当前线程的栈帧中创建一个Lock Record并将对象的Mark Word复制到其中。然后线程将尝试使用CAS操作来复制Mark。 Word指向Lock Record。如果CAS成功获得锁如果失败说明其他线程也在尝试获得轻量级锁会膨胀成重量级锁。重量级锁Heavyweight Locking 当多个线程竞争同一锁或轻量级锁CAS失败时锁将升级为重量级锁。此时JVM将调用操作系统底部的相互排斥Mutex实现线程的阻塞和唤醒。在锁被释放和唤醒之前阻塞的线程将被悬挂CPU资源将不再消耗。synchronized的锁升级机制旨在根据竞争程度动态调整锁的成本从低竞争时几乎没有成本到高竞争时的操作系统级同步。Java并发包中锁机制的底层实现原理剖析J.U.C包下的锁(基于AQS)java.util.concurrent.leeentrantlock等locks包提供了更灵活、更丰富的锁定机制、ReentrantReadWriteLock、Semaphore、CountDownlatch等。这些高级并发工具的基石是AbstractQuedsynchronizerAQS。AQS是一种抽象的队列同步器它提供了依赖先进先出的框架FIFO等待队列的阻塞锁和同步器。其核心思想包括整形state变量 用来表示同步状态。例如在Reentrantlock中state为0表示无锁大于0表示锁被持有其值表示重新进入次数state在semaphore中表示可用许可state在countdownlatch中表示计数器。一个CLHCraig, Landin, and HagerstenFIFO等待队列的风格 当线程同步状态失败时它将被包装成一个Node节点并添加到等待队列的尾部。基于CAS操作 所有通过CAS修改state变量和队列操作(如添加/删除节点)来保证原子性。LockSupport.park()LockSupport.unpark() AQS使用这两种方法来阻塞和唤醒等待队列中的线程而不是使用Object。.wait()和Object.notify()这使得线程的悬挂和唤醒更加准确避免了“假唤醒”等问题。通过实现tryAcquire()AQS的子类、tryRelease()等抽象方法定义具体的同步语义(独家模式或共享模式)而AQS本身负责管理队列、线程阻塞/唤醒的一般逻辑。如何实现Java中synchronized关键词的线程同步和锁升级Synchronized关键字在Java中实现线程同步其本质是通过JVM层面的Monitor(监视器)机制来完成的。每当创建一个对象时它在内存中都有一个对象头其中包含Mark Word和Klass Pointer等信息。Mark Word这个地方特别有趣。这是synchronized实现锁的关键。它将动态存储物体的哈希码、GC分代年龄以及最重要的锁标志位置和指向锁记录的指针或线程ID。当您用synchronized修改代码块或方法时JVM会在编译过程中生成两个字节码指令monitorenter和monitorexit。monitorenter指令试图获得锁如果成功将Mark 锁定标志位置设置为Word中的锁定状态。monitorexit指令是在同步块执行或抛出异常时释放锁。说到锁升级这真的是JVM为性能做的精致设计。它不是最重的锁而是根据实际竞争逐渐从轻到重升级:偏向锁Biased Locking 想象一下一个对象通常只被一个线程反复访问。在这种情况下每次去CAS或调用操作系统的原始语言成本太高了。因此JVM将“偏向”第一个获得锁的线程。当线程A第一次进入同步块时JVM将在对象头中使用Mark Word设置为偏向模式并记录下线程A的ID。之后只要线程A访问同步块它就可以直接进入而无需进行任何同步操作。这几乎是零成本的。但如果另一个线程B也试图获得锁偏向锁将被取消。取消过程需要等到整体安全点Safepoint然后暂停持有偏向锁的线程升级锁。轻量级锁Lightweight Locking 当偏向锁被取消或两条线程交替竞争但不是激烈竞争时锁将升级为轻量级锁。此时JVM不再直接调用操作系统而是使用CAS操作。每个线程将在自己的堆栈帧中创建一个Lock Record然后尝试使用CAS将对象的Mark Word指向这个Lockk Record。如果CAS成功说明锁定了。如果失败说明其他线程也在尝试CAS说明竞争激烈轻量级锁会进一步膨胀。重量级锁Heavyweight Locking 当轻量级锁的CAS操作失败或多线程持续激烈竞争时锁将膨胀为重量级锁。此时Mark Word将指向真实操作系统级的互斥量Mutex。那些没有抓到锁的线程会被堵塞park进入等待队列不再消耗CPU资源。当锁的线程释放锁时它会被唤醒unpark等待队列中的一个或多个线程重新竞争。这种锁升级的过程是一个动态适应的过程使得synchronized在不同的并发场景中表现出更好的性能。但值得一提的是一旦升级到重量级锁就无法降级通常是因为重量级锁成本高线程上下文切换成本高。AbstractQueuedSynchronizer (AQS) 在J.U.C包扮演了什么核心角色在我看来AbstractQueuedSynchronizerAQS并在Java发包J.U.C简直就是“基石”它定义了大多数高级并发工具的骨架和灵魂。可以说没有AQSJ.U.今天的C包不会有强大和灵活性。AQS本身不是一个锁而是一个用于构建锁和同步器的抽象框架。它包装了所有复杂的同步逻辑如排队、阻塞、唤醒和同步状态管理。开发人员只需要注意具体的同步语义即如何获取和释放资源而不用担心底部的线程调度细节。AQS的核心构成包括volatile int state变量 这是AQS的灵魂它代表同步状态。这种状态的具体含义完全由AQS的子类定义。例如Reentrantlock用它来表示锁的重新进入次数Semaphore用它来表示可用的许可数量Countdownlatch用它来表示需要等待的事件数量。volatile关键字保证了state变量在多线程之间的可见性。FIFO等待队列 这是一个双向链表用于存储未能获得同步状态并被阻塞的线程。每个线程都被包装成一个Node节点不仅包括线程本身还包括线程状态如是否需要唤醒、是否共享模式等。当一个线程试图获得同步状态失败时它将被包装成一个Node并添加到队列的尾部然后挂起。基于CAS的操作 通过CAS对State变量的所有修改以及等待队列的添加和删除操作Compare-And-Swap保证原子性的原语。这避免了传统重量级锁的使用从而提高了并发效率。LockSupport.park()LockSupport.unpark() AQS使用这两个底层工具来准确地阻止和唤醒等待队列中的线程。与Object相比.wait()和Object.notify()park/unpark更灵活不需要先获取对象的监控锁可以操作特定的线程避免死锁和虚假唤醒等问题。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2466132.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!