资讯动态

Java 中的锁

发布时间:2026/10/9 1:27:31 来源:尧图企业网站定制
1. 乐观锁和悲观锁悲观锁悲观锁认为自己在使用数据的时候一定有别的线程来修改数据因此在获取数据的时候会先加锁确保数据不会被别的线程修改。synchronized 关键字和Lock 的实现类都是悲观锁乐观锁乐观锁认为自己在使用数据时不会有别的线程修改数据所以不会添加锁只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新则根据不同的实现方式执行不同的操作。乐观锁在Java中是通过使用无锁编程来实现最常采用的是CAS算法Java原子类中的递增操作就通过CAS自旋实现的。乐观锁的实现方式①版本号②CAS2. 公平锁和非公平锁⽣活中排队讲求先来后到视为公平。程序中的公平性也是符合请求锁的绝对时间的其实就是 FIFO否则视为不公平。源码解读为什么要默认是非公平锁恢复挂起的线程到真正锁的获取还是有时间差的从开发人员来看这个时间微乎其微但是从CPU的角度来看这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU 的时间片尽量减少 CPU 空闲状态时间。使用多线程很重要的考量点是线程切换的开销当采用非公平锁时当1个线程请求锁获取同步状态然后释放同步状态因为不需要考虑是否还有前驱节点所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大所以就减少了线程的开销。使用场景如果为了更高的吞吐量很显然非公平锁是比较合适的因为节省很多线程切换时间吞吐量自然就上去了否则那就用公平锁大家公平使用。3. 可重入锁可重入锁又名递归锁是指在同一个线程在外层方法获取锁的时候再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象)不会因为之前已经获取过还没释放而阻塞。如果是1个有 synchronized 修饰的递归调用方法程序第2次进入被自己阻塞了岂不是天大的笑话出现了作茧自缚。所以 Java 中ReentrantLock 和 synchronized 都是可重入锁可重入锁的一个优点是可一定程度避免死锁。3.1 可重入锁之 synchronized简单的来说就是在一个 synchronized 修饰的方法或代码块的内部调用本类的其他 synchronized修饰的方法或代码块时是永远可以得到锁的与可重入锁相反不可重入锁不可递归调用递归调用就发生死锁。synchronized 可重入实现原理:每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。当执行 monitorenter 时如果目标锁对象的计数器为零那么说明它没有被其他线程所持有Java虚拟机会将该锁对象的持有线程设置为当前线程并且将其计数器加1。在目标锁对象的计数器不为零的情况下如果锁对象的持有线程是当前线程那么 Java 虚拟机可以将其计数器加1否则需要等待直至持有线程释放该锁。当执行 monitorexit 时Java 虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。3.2 可重入锁之 LockReentrantLock 也是可重入锁。ReentrantLock 的可重入性是通过以下机制实现的3.2.1 核心原理状态变量 (state)ReentrantLock 内部基于 AQS (AbstractQueuedSynchronizer) 实现利用其state变量跟踪锁的重入次数state 0锁未被任何线程持有。state 0锁被某线程持有数值表示该线程的重入次数。线程所有权检查AQS 维护一个exclusiveOwnerThread字段记录当前持有锁的线程。当线程尝试获取锁时会检查自身是否为锁的持有者。3.2.2 获取锁流程以非公平锁为例protectedfinalbooleantryAcquire(intacquires){finalThreadcurrentThread.currentThread();intcgetState();if(c0){// 锁未被持有if(compareAndSetState(0,acquires)){// CAS 抢锁setExclusiveOwnerThread(current);// 设置持有线程returntrue;}}elseif(currentgetExclusiveOwnerThread()){// 当前线程已持有锁intnextccacquires;// 增加重入次数if(nextc0)thrownewError(Maximum lock count exceeded);setState(nextc);// 更新 statereturntrue;}returnfalse;// 获取失败}3.2.3 释放锁流程protectedfinalbooleantryRelease(intreleases){intcgetState()-releases;if(Thread.currentThread()!getExclusiveOwnerThread())thrownewIllegalMonitorStateException();// 非持有线程释放锁抛出异常booleanfreefalse;if(c0){// 完全释放锁freetrue;setExclusiveOwnerThread(null);// 清除持有线程}setState(c);// 更新 statereturnfree;// 返回是否完全释放}3.2.4 关键点重入计数每次重入获取锁时state递增释放时递减直到归零后其他线程才能获取。线程绑定通过检查exclusiveOwnerThread确保只有持有线程能操作锁。异常处理非持有线程尝试释放锁会抛出异常防止非法操作。3.2.5 总结ReentrantLock 通过 AQS 的state变量记录重入次数结合线程所有权检查实现了可重入性。这使得同一线程可以多次安全地获取和释放锁避免了自死锁问题同时保持了锁状态的精确管理。4. 死锁略。。。5. 写锁 读锁ReentrantReadWriteLock6. 自旋锁CAS

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑