文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:学习AQS的使用场景以及自己通过AQS实现Semaphore。
一、Semaphore的原理和使用场景
1.Semaphore是什么?
Semaphore是一个计数信号量,常用于限制可以访问某些资源线程数目(限流)。是一种用来控制并发量的共享锁
2.如何通过AQS实现Semaphore
代码如下(示例):
public class BingSemaphore {
private Sync sync;
public BingSemaphore(int permits) {
this.sync = new Sync(permits);
}
//获取信号量
public void acquire() {
sync.acquireShared(1);
}
//释放信号量
public void release() {
sync.releaseShared(1);
}
//AQS里面有很多没有实现的方法,要使用AQS,一个创建AQS的实例,并且重写方法
class Sync extends AbstractQueuedSynchronizer {
//控制获取线程锁的个数大小
private int permits;
public Sync(int permits) {
this.permits = permits;
}
//这里不需要考虑入队列、出队列,
// 这些都是不带try的方法中实现了,作为了公共的业务逻辑
@Override
protected int tryAcquireShared(int arg) {
int c = getState(); //state表示信号量获取的个数
int nextc = c + arg;
if (nextc <= permits) {
if(compareAndSetState(c, nextc))
return 1;
}
return -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;){
int c = getState();
if(c == 0)
return false;
int nextc = c - arg;
if (compareAndSetState(c, nextc)){
return true;
}
}
}
}
}
二、CountDownLatch的原理和使用场景
1.CountDownLatch是什么?
CountDownLatch是一个倒计数开关(共享锁),使用场景:跑步起跑场景
2.如何通过AQS实现CountDownLatch
代码如下(示例):
public class BingCountDownLatch {
private Sync sync;
public BingCountDownLatch(int count){
this.sync=new Sync(count);
}
//释放共享锁
//state初始值为count,只有在state减为0的时候,才能释放锁成功
public void countDown(){
sync.releaseShared(1);
}
//获取共享,只有在state=0时,才能获取锁成功
public void await(){
sync.acquireShared(1);
}
class Sync extends AbstractQueuedSynchronizer {
public Sync(int count){
setState(count); //state用来记录倒计数
}
@Override
protected int tryAcquireShared(int arg) {
return getState() == 0 ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
for (;;){
int c = getState();
if (c == 0)
return false;
int nextc = c - arg;
//减1需要使用CAS操作
if (compareAndSetState(c, nextc)){
return nextc == 0;
}
}
}
}
}
三、CyclicBarrier的原理和使用场景
1.CyclicBarrier是什么?
CyclicBarrier是循环栅栏可以循环利用的屏障。举例:排队上车,四个人齐了就上车。
2.如何实现CyclicBarrier
CyclicBarrier是通过lock.newCondition()实现的, condition.await()方法让线程等待,通过判断如果满足条件调用condition.signalAll()唤醒线程;
代码如下(示例):
public class BingCyclicBarrier {
//condition实现
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
//一个批次的大小
private final int parties;
//记录当前一轮有多少个线程等待
private int count = 0;
//全局年代
private Object generation = new Object();
public BingCyclicBarrier(int parties){
if (parties <=0)
throw new IllegalArgumentException();
this.parties = parties;
}
//进入下一轮等待,叫做进入下一个 年代
public void nextGeneration(){
count = 0;
generation = new Object();
condition.signalAll();
}
public void await(){
lock.lock();
try {
Object myGeneration = generation;
int index = ++count;
//若当前一轮,集满
if (index == parties){
//进入下一轮 : count =0, 唤醒所有线程
nextGeneration();
return;
}
for (;;){
//没有集满,挂起线程
try {
condition.await(); //await方法用park来实现的
} catch (InterruptedException e) {
e.printStackTrace();
}
//什么时候应该让线程结束等待???
if (myGeneration != generation)
return;
}
}finally {
lock.unlock();
}
}
}