Java并发编程|第九篇:Semaphore 信号量

Java并发编程|第九篇:Semaphore 信号量

系列文章

1.介绍

semaphore 也可以说是信号量,可以控制线程访问的数量,它有两个方法,第一acquire() 获得一个许可,如果没有就等待,第二release()释放一个许可;有点类似限流的功能。

2.例子

这个例子可以理解为10辆车,去抢5个车位

/**
 * 限流
 * 1.设置线程个数new Semaphore(5)
 * 2.semaphore.acquire();线程获取lock
 * 3.semaphore.release();线程释放lock
 * 4.当线程获取5个锁之后,其他线程阻塞
 * */
public class SemaphoreDemo {

    static class Car extends Thread {
        private int num;
        private Semaphore semaphore;


        public Car(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                //抢占车位
                semaphore.acquire();
                System.out.println("第"+num+"抢占车位");
                Thread.sleep(100);//停车100ms
                semaphore.release();//车开走
                System.out.println("第"+num+"释放");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //设置5个车位
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            new Car(i, semaphore).start();
        }

    }
}

执行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwDqvb8p-1576741771200)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images)]

3.源码分析

以停车的代码为例

构造方法

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

最后调用的是Sync()构造,设置 setState 设置 state 值 为 5 ,有5个车位

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        Sync(int permits) {
            setState(permits);
        }
        ...此处省略N行代码

acquire()

这个方法和 CoutDownLatch 里面调用的方法是一样的

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

acquireSharedInterruptibly()

将线程加入到AQS共享锁队列

tryAcquireShared(arg)<0 表示没有可用的线程数量,执行doAcquireSharedInterruptibly()将线程加入AQS共享锁队列

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//没有可用的线程数量
            doAcquireSharedInterruptibly(arg);
    }

后面的方法就不做展开了可以参考

Java并发编程|第八篇:CountDownLatch

对应的方法即可

区别在于其中几个方法实现方式

Sync类

Sync这个类在Semaphore里面有两种实现

非公平NonfairSync
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

        NonfairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }
nonfairTryAcquireShared()

1.先获取目前的可用数量 state 值,比如说第一次为 5 个车位

2.然后 remaining = 5 - 1 = 4

3.判断 remining < 0 返回负值,表示没有可用的车位

4.CAS将available值替换为 4 如果成功,返回 4,表示有 4 个车位可以停车

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
公平FairSync

和非公平的区别就是,在执行之前先hasQueuedPredecessors()判断一下,是否有线程在排队

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

        FairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())//是否有线程在排队
                    return -1;
                int available = getState();//获得当前的state值
                int remaining = available - acquires;//可用的数量 - 已经获得锁的数量
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))//CAS 将可用数量替换为remaining 
                    return remaining;
            }
        }
    }

release()

释放,也是和 CoutDownLatch 中的countDown()完全一样,参照之前的文章即可

    public void release() {
        sync.releaseShared(1);
    }

tryReleaseShared()

这个方法是 Semaphore 自己的实现
通过tryReleaseShared()可以增加 1 个可用线程数,及当前 state + 1

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();//获得当前可以用的线程数
                int next = current + releases;//可以用线程数+1
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))//修改当前的可用线程数+1
                    return true;
            }
        }

总结

整个的实现原理和CoutDownLatch 非常相似,都是采用AQS的共享锁实现。

区别在于

第一:加入共享锁队列的条件不一样,当tryAcquireShared()<0时,表示没有可用线程数量,此时将线程加入到AQS的共享锁队列

第二:唤醒线程节点的条件也不一样,tryAcquireShared()>0,表示可用线程数 > 0,可用线程数量增加(通过tryReleaseShared()增加 1 个可用线程数),线程在去竞争锁,执行程序

4.参考

腾讯课堂->咕泡学院->mic老师->并发编程基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值