Semaphore信号量

Semaphore,等待指定数量的线程完成任务即可

复制代码
public class A {

    private static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 同步关键类,构造方法传入的数字是多少,则同一个时刻,只运行多少个进程同时运行制定代码
    private Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) throws Exception{
        A a = new A();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                a.doSomething();
            }).start();
        }
    }


    public void doSomething() {
        try {
            /**
             * 在 semaphore.acquire() 和 semaphore.release()之间的代码,同一时刻只允许制定个数的线程进入,
             * 因为semaphore的构造方法是1,则同一时刻只允许一个线程进入,其他线程只能等待。
             * */
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + ": start-" + getFormatTimeStr());
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + ": end-" + getFormatTimeStr());
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static String getFormatTimeStr() {
        return sf.format(new Date());
    }
}
View Code
复制代码

初始化

  初始化的时候,就是将 permits 设置给 state,标记占用锁标识。state的值就代表当前所剩余的令牌数量。

 

acquire()

获取令牌

  在 tryAcquireShared() 里面,第一个线程会先获取到当前的令牌数量,我们初始化的时候是2,remaining = 2-1>0 ,所以走cas操作赋值,等到第三个线程来的时候,则 remaining = 0-1>0,此时第三个线程就没获取到令牌了,走下面的 doAcquireSharedInterruptibly() 方法。

挂起线程

获取到令牌的线程就继续执行它的业务逻辑了,没有获取到令牌的线程就会来到这里。
1. addWaiter(Node.SHARED); 首先创建节点加入阻塞队列
2. node.predecessor(); 获取到上一个节点p,如果上一个节点是头节点,说明自己排在第一个。
3. 如果自己是第一个线程就 tryAcquireShared() 尝试cas获取令牌。(因为可能走到这里时,别人释放了令牌啊)
4. 然后 r>=0 就获取到了令牌,那就把当前线程从对等队列里面摘取出来
5. 如果不是第一个排队的节点,那就在 parkAndCheckInterrupt() 里面通过 park() 操作把自己挂起来,等待其他线程来把它唤醒

release()

释放令牌

  在 tryReleaseShared() 里面将 state 加一,然后cas操作执行成功就去唤醒等待队列中的节点。

唤醒其他节点

1. 判断头节点 h 的状态 Node.SIGNAL 是否需要唤醒后继节点
2. 进行 cas 操作修改状态为初始0
3. unparkSuccessor(h) 唤醒 h.next 节点

 

posted @   吴磊的  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
//生成目录索引列表
点击右上角即可分享
微信分享提示