JUC 三大辅助类
Semaphore
1、信号量
2、限制能同时访问共享资源的线程上限
3、应用:限流
(1)使用 Semaphore 限流,在访问高峰期时,让请求线程阻塞,渡过高峰期再释放许可
(2)只限于单机,不适合分布式
(3)只限制线程数,而不是限制资源数
(4)适用于线程数、数据库连接数是相等的,即一个线程,对应一个数据库连接
4、获取许可:acquire
5、释放许可:release
6、构造器
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
NonfairSync(int permits) {
super(permits);
}
Semaphore 加锁
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//若可分配线程数 < 0
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
1、非公平锁
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//获取state,即构造器中的permits
int available = getState();
//state-1
int remaining = available - acquires;
if (
//剩余线程不足以分配
remaining < 0 ||
//尝试修改可用线程数
compareAndSetState(available, remaining))
//返回剩余线程数
return remaining;
}
}
2、公平锁
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//添加共享节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//获取node的前驱节点
final Node p = node.predecessor();
//判断p是否为head
if (p == head) {
//再次尝试获取可用线程
int r = tryAcquireShared(arg);
if (r >= 0) {
//获取成功
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//获取失败
if (
//再次获取可用线程,失败则将前驱节点state改为-1
shouldParkAfterFailedAcquire(p, node) &&
//在此阻塞
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
Semaphore 释放锁
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;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
//队列还有除head以外的节点
if (h != null && h != tail) {
//获取head的waitStatus
int ws = h.waitStatus;
//若ws == -1
if (ws == Node.SIGNAL) {
//尝试将ws从-1改为0,防止其他线程干扰,即防止多次唤醒第二个节点
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
//修改失败,则继续尝试
continue; // loop to recheck cases
//修改成功,唤醒head的后继节点
unparkSuccessor(h);
}
//尝试将ws从0改为-3,以解决传播性,
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
//修改失败,继续尝试
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
private void setHeadAndPropagate(Node node, int propagate) {
//记录原head,以方便在下面检查
Node h = head;
//设置node为head
setHead(node);
//propagate表示有共享资源(例如共享读锁或信号量)
//原 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE
//现 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
//获取node的后继节点
Node s = node.next;
if (
//node已经是最后的节点
s == null ||
//s是共享节点
s.isShared())
//唤醒后继的所有共享节点,直到遇见独占节点
doReleaseShared();
}
}
CountdownLatch
1、作用:线程同步协作,等待所有线程完成倒计时
2、构造器初始化等待计数值
public CountDownLatch(int count)
3、等待当前线程等到锁存器计数到零,除非线程被 interrupt
public void await() throws InterruptedException
4、等待当前线程等到锁存器向下计数到零,除非线程被 interrupt ,否则等待指定时间结束
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
5、减少锁存器的计数,如果计数达到零,释放所有等待的线程
public void countDown()
6、使用
(1)一般配合线程池,如:等待多个远程调用结束
(2)可代替底层 join、wait、notify
(3)适合无返回结果,可以使用 Future 接收结果
(4)在创建时就固定计数,不可重用,即不能重置计数
CyclicBarrier
1、循环栅栏
2、作用:线程协作,等待线程满足某个计数
3、创建一个新的 CyclicBarrier ,当给定数量的线程(线程)正在等待时,它将跳闸,并且当屏障跳闸时不执行预定义的动作
public CyclicBarrier(int parties)
4、创建一个新的 CyclicBarrier ,当给定数量的线程(线程)正在等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行
public CyclicBarrier(int parties, Runnable barrierAction)
5、等待所有 parties 已经在这个障碍上调用 await
public int await() throws InterruptedException, BrokenBarrierException
6、等待所有 parties 已经在此屏障上调用 await,或指定的等待时间过去
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
7、使用
(1)在创建时就固定计数,初始值不能改变
(2)可重用,在计数归 0 时,重置计数为初始值
(3)等待线程数,要与初始化值相等
释义
1、CountdownLatch
(1)一个线程调用 await,计数值归 0 前阻塞,归 0 后继续运行
(2)其余线程调用 countDown,将计数器值 -1,继续运行
2、CyclicBarrier
(1)线程调用 await,计数值 -1,归 0 前阻塞
(2)计数值归 0 后,即足够数量的线程等待时,它将跳闸
(3)当屏障跳闸时,不执行预定义的动作;或由最后一个进入屏障的线程,执行给定的屏障动作
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战