Java多线程篇(7)——AQS之共享锁(Semaphore、CountDownLatch)

文章目录

1、Semaphore

1.1、acquire

Semaphore.acquire

java 复制代码
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

AbstractQueuedSynchronizer.acquireSharedInterruptibly

java 复制代码
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //尝试获取共享资源(默认非公平的实现)    
        if (tryAcquireShared(arg) < 0)
            //若获取资源失败进入这个方法
            doAcquireSharedInterruptibly(arg);
    }

Semaphore.tryAcquireShared

java 复制代码
   //Semaphore非公平锁的实现
   protected int tryAcquireShared(int acquires) {
       return nonfairTryAcquireShared(acquires);
   }
   final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
           int available = getState();
           int remaining = available - acquires;
           if (remaining < 0 ||
             compareAndSetState(available, remaining))
             return remaining;
          }
    }

	//Semaphore公平锁的实现
    protected int tryAcquireShared(int acquires) {
       for (;;) {
            if (hasQueuedPredecessors())
               return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
               compareAndSetState(available, remaining))
               return remaining;
        }
    }

AbstractQueuedSynchronizer.doAcquireSharedInterruptibly

java 复制代码
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //封装共享节点并入队
        final Node node = addWaiter(Node.SHARED);
        try {
            for (;;) {
                final Node p = node.predecessor();
                //如果前一个节点是头结点,再次尝试获锁
                if (p == head) {
                    //获取共享资源
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //如果获取成功设置头节点并传播
                        //其实就是成功获取资源后接着唤醒后面的节点,因为是共享资源,所以要尽可能唤醒更多的节点
                        setHeadAndPropagate(node, r);
                        p.next = null;
                        return;
                    }
                }
                //如果获锁失败或者前节点不是head的节点就根据前节点的状态来看是否需要阻塞
                //需要阻塞就调用 LockSupport.park() 阻塞线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }

被唤醒后...
AbstractQueuedSynchronizer.setHeadAndPropagate

java 复制代码
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head;
        //设置头结点
        setHead(node);

		//唤醒后继节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            //如果是共享节点调用doReleaseShared唤醒后继节点
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

AbstractQueuedSynchronizer.doReleaseShared

java 复制代码
 private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //cas(SIGNAL->0) + unpark 
                if (ws == Node.SIGNAL) {
                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                        continue;
                    unparkSuccessor(h);
                }
                //cas(0->PROPAGATE)
                else if (ws == 0 &&
                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                    continue;
            }
            //如果head不变,说明已经没有需要唤醒的节点了,则break
            if (h == head)
                break;
        }
    }

1.2、release

Semaphore.release

java 复制代码
    public void release() {
        sync.releaseShared(1);
    }

AbstractQueuedSynchronizer.releaseShared

java 复制代码
    public final boolean releaseShared(int arg) {
        //释放共享资源
        if (tryReleaseShared(arg)) {
            //成功释放则调用 doReleaseShared 唤醒节点
            doReleaseShared();
            return true;
        }
        return false;
    }

Semaphore.tryReleaseShared

java 复制代码
	protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            int next = current + releases;
            if (next < current) // overflow
                throw new Error("Maximum permit count exceeded");
            if (compareAndSetState(current, next))
                return true;
        }
    }

可以发现独占锁只会唤醒一个节点,而共享锁会类似传播一样从第一个被唤醒的节点开始逐次唤醒后面的节点。

2、CountDownLatch

2.1、await

CountDownLatch.await

java 复制代码
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

acquireSharedInterruptibly上面已经分析过了,这里直接看到CountDownLatch的获取共享资源的实现
CountDownLatch.tryAcquireShared

java 复制代码
   protected int tryAcquireShared(int acquires) {
        //如果state为0,则允许获取,反之不允许
        return (getState() == 0) ? 1 : -1;
   }

2.2、countDown

CountDownLatch.countDown

java 复制代码
    public void countDown() {
        sync.releaseShared(1);
    }

releaseShared上面也已经分析过了,这里同样直接看到CountDownLatch的释放共享资源的实现
CountDownLatch.tryReleaseShared

java 复制代码
	protected boolean tryReleaseShared(int releases) {
       for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            // state - 1
            int nextc = c - 1;
            //cas替换state
            if (compareAndSetState(c, nextc))
                //如果替换后state为0,则返回true,表示可以唤醒节点了
                return nextc == 0;
        }
    }
相关推荐
从琳开始98912 分钟前
网络原理入门(二)------OSI七层模型?TCP/IP五层(四层)模型原来是这样的
java·网络·tcp/ip·java-ee·php
文丑颜不良啊24 分钟前
LeetCode1861~1880题目解析
java·leetcode
Flynn Liang26 分钟前
AgentScope Java 深度解析:ReActAgent、HarnessAgent 与多智能体编排——JVM 上的自治 Agent 底座
java·大模型·架构设计·ai agent·spring ai·langchain4j·agentscope
Flynn Liang39 分钟前
上线后怎么排查 AI 调用?Spring AI Observability:token/延迟/成本埋点与全链路追踪
java·人工智能·spring·监控·可观测性·observability·springai
文丑颜不良啊42 分钟前
LeetCode1921~1940题目解析
java·leetcode
Tt有了t1 小时前
设计模式速查
java·python·设计模式
Thomas.Sir1 小时前
第15课:SpringBoot连接池详解:HikariCP配置、调优、监控,4.x连接池新特性
java·spring boot
VimorphTech2 小时前
每批开 4 个线程,为什么限制不住 TTS 的总并发?
java·spring boot·spring
Pniubi2 小时前
Java为什么要分堆和栈
java·开发语言
杨凯凡2 小时前
Spring Boot 缓存一致性怎么保证?@Cacheable、@CacheEvict 与事务提交时机实战
java·spring boot·spring cache·后端开发·缓存一致性