Semaphore的应用
Semaphore semaphore=new Semaphore(num);
num在这里是aqs的state变量;这里代表state默认值为num
每次同时运行的线程为上限为num个,最大并行度为num次;
Semaphore semaphore=new Semaphore(num);
semaphore.acquire();//获取资源
/这里可以理解成这里有num把锁,抢到的可以运行主线程代码
semaphore.release();//释放资源
semaphore.acquire(num2);//每次获取资源是num2个,就是每次state会减掉num2次
semaphore.release(num3);//每次释放num3个
执行时间过长如果在某个场景下,有10个线程,每次运行两个线程,如果在执行过程中有个时间太长了;我们可以设置最长等待时间;
semaphore.tryAcquire(500, TimeUnit.MICROSECONDS);(返回布尔值)
执行这个最长运行等待500ms的时长;如果超过这500ms就会不等了;我们可以执行降级的方法;
其他方法
semaphore.acquireUninterruptibly();
这个方法也是获取资源和semaphore.acquire();类似,但是 semaphore.acquireUninterruptibly();方法是不会因为中断而抛异常;semaphore.acquire();会因为接收过中断信号而抛出异常;
Semaphore源码
**semaphore.acquire();**获取资源
流程:
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
//判断该方法是否被中断过,如果中断过那么直接抛出异常
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
//尝试获取锁,返回的值是state-每次消耗的资源量的值,如果是负数说明state不够资源使用
doAcquireSharedInterruptibly(arg);
//没有获取到共享锁,加入同步阻塞队列
}
**tryAcquireShared(arg)**尝试获取锁,有两种实现一种是公平的一种是非公平的
公平的:
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
//判断是否有线程还在队列中等待,自己是不是优先级最高线程不是直接返回-1,去阻塞线程
return -1;
int available = getState();
//获取当前state
int remaining = available - acquires;
//当前state减去需要的资源量
if (remaining < 0 ||
//小于0明显不行返回负数,去阻塞线程
compareAndSetState(available, remaining))
//如果大于0执行这一步,把当前状态改成消减掉资源后剩余数量
return remaining;
}
}
非公平的
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
//获取当前state剩余资源,不需要去判断是否同步队列中有线程在等待
int remaining = available - acquires;
//减掉需要的量还剩多少
if (remaining < 0 ||
//同上
compareAndSetState(available, remaining))
//同上
return remaining;
}
}
**doAcquireSharedInterruptibly(arg)**创建线程阻塞队列
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
//创建共享的节点
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//获取前驱节点
if (p == head) {
//如果前驱节点是头节点
int r = tryAcquireShared(arg);
//尝试获取锁,并且返回剩余信号量(state)
if (r >= 0) {
//那么state足够这次的消耗
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);//把当前节点设置成头节点,并且删除其里面的线程,prev属性
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
//如果锁计数>0,就继续唤醒下面的线程
Node s = node.next;
if (s == null || s.isShared())
//检查下一个节点是否是 shared,如果是将 head 的状态从 -1 改为 0 并唤醒老二
doReleaseShared();
}
}
doReleaseShared
唤醒其后后继节点,具体的说是需要唤醒其后到下一个尝试获取锁的的节点之间的所有尝试获取
读锁的线程。
private void doReleaseShared() {
for (;;) {
Node h = head;//获取头部节点
if (h != null && h != tail) {//头部节点不为空,且不等于尾部节点,说明队列中至少有一个在等待
int ws = h.waitStatus;
//获取头部节点的waitStatus
if (ws == Node.SIGNAL) {
//如果是-1那么
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
//把头部节点的waitStatus改成-3,
continue;
}
if (h == head)
break;
}
}
**semaphore.release();**释放资源
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();//
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;//把信号量还回去
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
CountDownLatch计数器
CountDownLatch countDownLatch=new CountDownLatch(num);
countDownLatch.countDown();每执行一次,每个线程运行完毕就会相当是执行一次,num就会减1,当num减完为0时就会执行 countDownLatch.await();方法;
还有一种倒过来的方式去用,比如我们在主线程写一个延时countDownLatch.countDown();然后所有调用了countDownLatch.await();的方法会等到主线程运行完countDownLatch.countDown();方法再来执行countDownLatch.await();方法,其他线程都会被阻塞在那个位置等着;
CyclicBarrier
CyclicBarrier cyclicBarrier=new CyclicBarrier(11 );
cyclicBarrier.await();
当有很多线程进来的时候会等到聚齐11个线程才会通过方法 cyclicBarrier.await();这11个线程全部同时执行;
cyclicBarrier.await();可以反复的使用,countDownLatch不能反复的使用;