AQS共享锁应用之Semaphore原理
我们调用Semaphore方法时,其实是在间接调用其内部类或AQS方法执行的。Semaphore类结构与ReetrantLock类相似,内部类Sync继承自AQS,然后其子类FairSync和NoFairSync分别实现公平锁和非公平锁的获取锁方法tryAcquireShared(int arg)
,而释放锁的tryReleaseShared(int arg)
方法则有Sync类实现,因为非公平或公平锁的释放过程都是相同的。
AQS通过state值来控制对共享资源访问的线程数,有线程请求同步状态成功state值减1,若超过共享资源数量获取同步状态失败,则将线程封装共享模式的Node结点加入到同步队列等待。有线程执行完任务释放同步状态后,state值会增加1,同步队列中的线程才有机会获得执行权。公平锁与非公平锁不同在于公平锁申请获取同步状态前都会先判断同步队列中释放存在Node,若有则将当前线程封装成Node结点入队,从而保证按FIFO的方式获取同步状态,而非公平锁则可以直接通过竞争获取线程执行权。
//Semaphore的acquire() public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** * 注意Sync类继承自AQS * AQS的acquireSharedInterruptibly()方法 */ public final void acquireSharedInterruptibly(int arg) throws InterruptedException { //判断是否中断请求 if (Thread.interrupted()) throw new InterruptedException(); //如果tryAcquireShared(arg)不小于0,则线程获取同步状态成功 if (tryAcquireShared(arg) < 0) //未获取成功加入同步队列等待 doAcquireSharedInterruptibly(arg); } //Semaphore中非公平锁NonfairSync的tryAcquireShared() protected int tryAcquireShared(int acquires) { //调用了父类Sync中的实现方法 return nonfairTryAcquireShared(acquires); } final int nonfairTryAcquireShared(int acquires) { //使用死循环 for (;;) { int available = getState(); int remaining = available - acquires; //判断信号量是否已小于0或者CAS执行是否成功 if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { //创建共享模式的结点Node.SHARED,并加入同步队列 final Node node = addWaiter(Node.SHARED); boolean failed = true; try { //进入自旋操作 for (;;) { final Node p = node.predecessor(); //判断前驱结点是否为head if (p == head) { //尝试获取同步状态 int r = tryAcquireShared(arg); //如果r>0 说明获取同步状态成功 if (r >= 0) { //将当前线程结点设置为头结点并传播 setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } //调整同步队列中node结点的状态并判断是否应该被挂起 //并判断是否需要被中断,如果中断直接抛出异常,当前结点请求也就结束 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) //结束该结点线程的请求 cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //获取当前结点的等待状态 int ws = pred.waitStatus; //如果为等待唤醒(SIGNAL)状态则返回true if (ws == Node.SIGNAL) return true; //如果ws>0 则说明是结束状态, //遍历前驱结点直到找到没有结束状态的结点 if (ws > 0) { do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //如果ws小于0又不是SIGNAL状态, //则将其设置为SIGNAL状态,代表该结点的线程正在等待唤醒。 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } private final boolean parkAndCheckInterrupt() { //将当前线程挂起 LockSupport.park(this); //获取线程中断状态,interrupted()是判断当前中断状态, //并非中断线程,因此可能true也可能false,并返回 return Thread.interrupted(); } //不可中的acquireShared() public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC if (interrupted) selfInterrupt(); failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //没有抛出异常中的。。。。 interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below setHead(node);//设置为头结点 /* * 尝试去唤醒队列中的下一个节点,如果满足如下条件: * 调用者明确表示"传递"(propagate > 0), * 或者h.waitStatus为PROPAGATE(被上一个操作设置) * 并且 * 下一个节点处于共享模式或者为null。 * * 这两项检查中的保守主义可能会导致不必要的唤醒,但只有在有 * 有在多个线程争取获得/释放同步状态时才会发生,所以大多 * 数情况下会立马获得需要的信号 */ if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; if (s == null || s.isShared()) //唤醒后继节点,因为是共享模式,所以允许多个线程同时获取同步状态 doReleaseShared(); } } //Semaphore的release() public void release() { sync.releaseShared(1); } //调用到AQS中的releaseShared(int arg) public final boolean releaseShared(int arg) { //调用子类Semaphore实现的tryReleaseShared方法尝试释放同步状态 if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } //在Semaphore的内部类Sync中实现的 protected final boolean tryReleaseShared(int releases) { for (;;) { //获取当前state int current = getState(); //释放状态state增加releases int next = current + releases; if (next < current) // overflow throw new Error("Maximum permit count exceeded"); //通过CAS更新state的值 if (compareAndSetState(current, next)) return true; } } private void doReleaseShared() { /* * 保证释放动作(向同步等待队列尾部)传递,即使没有其他正在进行的 * 请求或释放动作。如果头节点的后继节点需要唤醒,那么执行唤醒 * 动作;如果不需要,将头结点的等待状态设置为PROPAGATE保证 * 唤醒传递。另外,为了防止过程中有新节点进入(队列),这里必 * 需做循环,所以,和其他unparkSuccessor方法使用方式不一样 * 的是,如果(头结点)等待状态设置失败,重新检测。 */ for (;;) { Node h = head; if (h != null && h != tail) { // 获取头节点对应的线程的状态 int ws = h.waitStatus; // 如果头节点对应的线程是SIGNAL状态,则意味着头 //结点的后继结点所对应的线程需要被unpark唤醒。 if (ws == Node.SIGNAL) { // 修改头结点对应的线程状态设置为0。失败的话,则继续循环。 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // 唤醒头结点h的后继结点所对应的线程 unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } // 如果头结点发生变化,则继续循环。否则,退出循环。 if (h == head) // loop if head changed break; } } //唤醒传入结点的后继结点对应的线程 private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); //拿到后继结点 Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) //唤醒该线程 LockSupport.unpark(s.thread); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2017-06-12 《幸福的方法》晨读笔记
2016-06-12 2016第23周日
2015-06-12 2015第24周五Spring的AOP
2014-06-12 第24周四经验感受想法
2013-06-12 2013年6月12日星期三
2012-06-12 尝试在手机上发表blog