理解AQS(AbstractQueuedSynchronizer)
可重入锁又名递归锁:是指在同一线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。
Java中ReentrantLock和synchranized都是可重入锁,可重入锁得一个优点是可一定程度避免死锁
synchronized的重入的实现原理:
每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。
当执行monitorexit时,java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。
唤醒线程的三种方式:
Object类中的wait、notify、notifyAll用于线程等待和环形的方法,都必须在synchronized内部执行(必须用到关键字synchronized)
public class LockSupportDemo { static Object objectlock=new Object(); public static void main(String[] args) { new Thread(()->{ synchronized (objectlock){ System.out.println(Thread.currentThread().getName()+"\t"+"--------come in"); try { objectlock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"--------被唤醒"); } },"A").start(); new Thread(()->{ synchronized (objectlock) { objectlock.notify(); System.out.println(Thread.currentThread().getName()+"\t"+"--------通知"); } },"B").start(); } }
传统的synchronized和Lock实现等待唤醒通知的约束,线程先要获得并持有锁,必须在块(synchronized或lock)中,必须要先等待后唤醒,线程才能够被唤醒
public static void lockAwaitSignal() { new Thread(()->{ lock.lock(); try { System.out.println(Thread.currentThread().getName()+"\t"+"--------come in"); try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"--------被唤醒"); }finally { lock.unlock(); } },"A").start(); new Thread(()->{ lock.lock(); try { condition.signal(); System.out.println(Thread.currentThread().getName()+"\t"+"--------通知"); }finally { lock.unlock(); } },"B").start(); }
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对象的唤醒方法。归根结底,LockSupport调用的Unsafe中的native代码。
LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpack就加1变成1,调用一个park会消费permit,也就是将1变成0,同时park立即返回,如再次调用park会变成阻塞(因为permit为零了会阻塞在这里,一直到permit变为1),这时调用unpark会把permit置为1,每个线程都有一个相关的permit,permit最多只有一个,重复调用unpark也不会积累凭证
AQS是用来构建锁(ReentrantLock)或者其它同步器组件(semaphore、countdownlatch等)的重量级基础框架及整个JUC体系的基石,通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义