根据AQS推测Semaphore及源码分析
来源:互联网 发布:淘宝官方优惠券app 编辑:程序博客网 时间:2024/06/09 17:14
Semaphore意为信号量,用法和CountDownLatch类似,也可以用来控制线程之间的协作关系,但通常用来控制同时访问的线程的数量。
先看看示例:
public class SemaphoreTest { public static void main(String[] args) { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5个线程同时访问 final Semaphore semp = new Semaphore(5); // 模拟20个客户端访问 for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // 获取许可 semp.acquire(); System.out.println("Accessing: " + NO); Thread.sleep((long) (Math.random() * 10000)); // 访问完后,释放 ,如果屏蔽下面的语句,则在控制台只能打印5条记录,之后线程一直阻塞 semp.release(); } catch (InterruptedException e) { } } }; exec.execute(run); } // 退出线程池 exec.shutdown(); } }
可以看到,在执行run方法里面的时候,需要先获取许可,只有获取之后才能接着执行,执行完之后再释放许可供后面的线程获取。
其实,Semaphore也是继承的AQS,然后实现其中的共享模式(和CountDownLatch一样,因为他们都支持多个线程获取资源,而不像reentrankLock,同时只能有一个线程。)
那么semp.acquire()方法是会阻塞线程的。而release()方法则会唤醒后继节点。
再给出根据http://blog.csdn.net/FoolishAndStupid/article/details/75676027 中得到的简单的结论(只是简单的结论,实际上要复杂一些):
我们推测:semp.acquire()方法是调用的AQS的acquireShared()方法,然后调用自定义同步器中实现的tryAcquireShared()方法,然后返回许可数量。当许可数量=0时表示没有资源了,则tryAcquireShared()返回<0,从而阻塞调用semp.acquire()的线程。
再看semp.release()方法:推测它是调用的AQS的releaseShared()方法,然后调用自定义同步器中的实现的tryReleaseShared()方法,当资源数>0时,返回true,从而唤醒调用acquire()而阻塞的线程。
给出推论之后再来看看源码:
semp.acquire()方法:
public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
再来看看AQS中的acquireSharedInterruptibly()方法:
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
其实就是调用的自定义同步器中实现的tryAcquireShared()方法。可以看到,semaphore中有一个公平的获取资源和非公平的获取资源2种:
公平的:protected int tryAcquireShared(int acquires) { Thread current = Thread.currentThread(); for (;;) { Thread first = getFirstQueuedThread(); if (first != null && first != current) return -1; int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }非公平的:final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }
公平和非公平的获取资源的区别在于:非公平的方式会直接用CAS的方式去获取资源,然后返回剩余的资源数。而公平的方式则是先从等待队列中取出第一个线程,然后判断是否为当前线程,如果不是,就直接返回-1,然后将它加入到等待队列中末尾。公平的方式其实就是根据队列中等待的先后顺序来分配资源。
还是符合我们图片中描述的。
再来看看release()方法:
public void release() { sync.releaseShared(1); }
AQS中的releaseShared():
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }
就是自定义同步器中实现的tryReleaseShared()方法:
不过这个并没有公平和非公平之分。因为释放资源都是将自身线程中的资源释放。和等待队列没关系。
protected final boolean tryReleaseShared(int releases) { for (;;) { int p = getState(); if (compareAndSetState(p, p + releases)) return true; } }
可以看到就使用for循环+CAS的方式,直到设置成功,则返回true,然后就可以唤醒后继节点。
和图片中描述的相同
所以只要搞清楚自定义同步器需要实现的接口和AQS中的关系,AQS为我们做了哪些步骤,那对于理解j.u.c中各个同步类具有很大的帮助,我们也能用AQS来实现自己需要的同步器。
- 根据AQS推测Semaphore及源码分析
- 根据AQS推测CountDownLatch及源码分析
- AQS源码分析
- Java AQS源码分析
- AQS(AbstractQueuedSynchronizer)源码分析
- AQS源码分析
- AQS源码分析
- AQS源码分析
- AQS源码分析
- AQS源码分析
- JUC - AbstractQueuedSynchronizer(AQS) 源码分析
- AQS源码分析之ConditionObject
- 《Java源码分析》:Semaphore
- JUC - Semaphore 源码分析
- Semaphore 源码分析
- 《Java源码分析》:Semaphore
- 并发编程工具之三:Semaphore 用法及源码分析
- concurrent-6-AQS-Semaphore
- 降薪求职对于应届毕业生来说是个选择
- 网页静态化和网页伪静态化之间的区别与选择
- PAT(Basic Level)_1007_素数对猜想
- 前端学习17/07/22
- PAT(Basic Level)_1008_数组元素循环右移问题
- 根据AQS推测Semaphore及源码分析
- PAT(Basic Level)_1009_说反话
- Python 3.x 连接数据库(pymysql 方式)
- Goodbye,OI!
- String类的常用的判断功能
- scp 与 rsync
- zoj 2112 树状数组 套主席树 动态求区间 第k个数
- Service
- python function 传入多个参数; 动态传入不同个数参数