上篇介绍了内部使用AQS的并发工具类CountDownLatch,接下来我要介绍的并发工具类Semaphore内部同样使用了AQS。
简介
信号量(Semaphore),有时被称为信号灯,一个控制访问多个共享资源的计数器,负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。
从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。通过acquire()和release()获取和释放访问许可。
继续拿上篇中的吃火锅案例,假设这家小肥羊的洗手间比较高级,洗手间一共有五个坑位,洗手间门有一个信号灯,就跟火车上那个相似,但是火车上的高级,可以显示洗手间当前的可用坑位个数。那么这个五个坑位就是公共资源,信号灯就是Semaphore,要用洗手间的顾客就是线程。
构造方法

如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
acquire()获取资源

还是老套路,调用AQS的acquireSharedInterruptibly(int arg)

公平锁和非公平锁的获取资源是不一样,先看公平锁的:

非公平锁的:

release() 释放资源


公平锁和非公平锁的获取资源不一样,但释放资源是一样的。

案例
public class AA implements Runnable {
private Semaphore semaphore;
private int id;
public AA(Semaphore semaphore, int id) {
this.semaphore = semaphore;
this.id = id;
}
@Override
public void run() {
try {
//获取坑位
semaphore.acquire();
System.out.println("第 " + id + " 个人正在使用");
Thread.sleep((long)(Math.random() * 1000));
//使用完后,出洗手间,释放坑位
semaphore.release();
System.out.println("第 " + id + " 个人用完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class GoWC {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
ExecutorService se = Executors.newCachedThreadPool();
se.submit(new AA(semaphore,1));
se.submit(new AA(semaphore,2));
se.submit(new AA(semaphore,3));
se.submit(new AA(semaphore,4));
se.submit(new AA(semaphore,5));
se.submit(new AA(semaphore,6));
se.submit(new AA(semaphore,7));
se.submit(new AA(semaphore,8));
se.submit(new AA(semaphore,9));
se.submit(new AA(semaphore,10));
se.shutdown();
}
}
运行结果:

对于这个结果,感觉有点不太对,比如第5个人释放后,第6个人使用是没问题的,但为什么第7个人也是正在使用???这时同时存在6个线程一块执行,不知道哪写错了。。哪位同学看出问题了,望指出,3Q!
总结
本篇主要介绍了基于AQS的并发工具类Semaphore,起一个信号灯的作用,有公平模式和非公平模式之分,获取公共资源使用acquire,公平模式下的acquire比非公平模式下的acquire多了检查当前线程是否在队列最前端这一步骤,释放资源使用release,公平模式和非公平模式一致。
网友评论