龙泉建设局网站,做淘宝链接网站,网页设计 网站建设,wordpress 24小时插件3.3.3 Semaphore公平实现
公平与非公平只是差了一个方法的实现tryAcquireShared实现 这个方法的实现中#xff0c;如果是公平实现#xff0c;需要先查看AQS中排队的情况
// 信号量公平实现
protected int tryAcquireShared(int acquires) {
// 死循环。
for (;;) {
// 公平…3.3.3 Semaphore公平实现
公平与非公平只是差了一个方法的实现tryAcquireShared实现 这个方法的实现中如果是公平实现需要先查看AQS中排队的情况
// 信号量公平实现
protected int tryAcquireShared(int acquires) {
// 死循环。
for (;;) {
// 公平实现在走下述逻辑前先判断队列中排队的情况
// 如果没有排队的节点直接不走if逻辑
// 如果有排队的节点发现当前节点处在head.next位置直接不走if逻辑if (hasQueuedPredecessors())
return -1;
// 下面这套逻辑和公平实现是一模一样的。
int available getState();
int remaining available - acquires;
if (remaining 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
3.3.4 Semaphore释放资源
因为信号量从头到尾都是共享锁的实现…… 释放资源操作不区分公平和非公平
// 信号量释放资源的方法入口
public void release() {
sync.releaseShared(1);
}
// 释放资源不分公平和非公平都走AQS的releaseShared
public final boolean releaseShared(int arg) {
// 优先查看tryReleaseShared这个方法是信号量自行实现的。
if (tryReleaseShared(arg)) {// 只要释放资源成功执行doReleaseShared唤醒AQS中排队的线程去竞争Semaphore的资源
doReleaseShared();
return true;
}
return false;
}
// 信号量实现的释放资源方法
protected final boolean tryReleaseShared(int releases) {
// 死循环
for (;;) {
// 拿到当前的state
int current getState();
// 将state 归还的资源个数新的state要被设置为next
int next current releases;
// 如果归还后的资源个数小于之前的资源数。
// 避免出现归还资源后导致next为负数需要做健壮性判断
if (next current)
throw new Error(Maximum permit count exceeded);
// CAS操作保证原子性只会有一个线程成功的就之前的state修改为next
if (compareAndSetState(current, next))
return true;
}
}
3.4 AQS中PROPAGATE节点
为了更好的了解PROPAGATE节点状态的意义优先从JDK1.5去分析一下释放资源以及排队后获取资源的后置操作
3.4.1 掌握JDK1.5-Semaphore执行流程图
首先查看4个线程获取信号量资源的情况 [image] 往下查看释放资源的过程会触发什么问题 首先t1释放资源做了进一步处理 [image] 当线程3获取锁资源后线程2再次释放资源因为执行点问题导致线程4无法被唤醒
3.4.2 分析JDK1.8的变化
JDK1.5实现.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
Node h head;
if (h ! null h.waitStatus ! 0)
unparkSuccessor(h);
return true;}
return false;
}
private void setHeadAndPropagate(Node node, int propagate) {
setHead(node);
if (propagate 0 node.waitStatus ! 0) {
Node s node.next;
if (s null || s.isShared())
unparkSuccessor(node);
}
}
JDK1.8实现.
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
// 拿到head节点
Node h head;
// 判断AQS中有排队的Node节点if (h ! null h ! tail) {
// 拿到head节点的状态
int ws h.waitStatus;
// 状态为-1
if (ws Node.SIGNAL) {
// 将head节点的状态从-1改为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 唤醒后继节点
unparkSuccessor(h);
}
// 发现head状态为0将head状态从0改为-3目的是为了往后面传播
else if (ws 0
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 没有并发的时候。head节点没变化正常完成释放排队的线程
if (h head)
break;
}
}
private void setHeadAndPropagate(Node node, int propagate) {
// 拿到head
Node h head;
// 将线程3的Node设置为新的head
setHead(node);// 如果propagate 大于0代表还有剩余资源直接唤醒后续节点如果不满足也需要继续往后判断看下是否需要传播
// h null看成健壮性判断即可
// 之前的head节点状态为负数说明并发情况下可能还有资源需要继续向后唤醒Node
// 如果当前新head节点的状态为负数继续释放后续节点
if (propagate 0 || h null || h.waitStatus 0 || (h head) null || h.waitStatus 0) {
// 唤醒当前节点的后继节点
Node s node.next;
if (s null || s.isShared())
doReleaseShared();
}
}
4 Semaphore在Java并发编程中的使用和比较技术
Semaphore vs. Lock
Semaphore和Lock如ReentrantLock都可以用于线程之间的互斥访问控制。它们的区别在于 Semaphore允许多个线程同时访问资源而Lock一次只允许一个线程访问资源。 Semaphore是基于计数的机制可以控制同时访问的线程数量而Lock只是简单的互斥锁。 根据具体场景选择Semaphore还是Lock取决于对资源的访问控制需求。
Semaphore vs. Condition
Semaphore和Condition都可以用于线程之间的同步和通信但在不同的场景下有不同的用途 Semaphore主要用于控制对资源的访问限制并发线程的数量。 Condition主要用于线程之间的协调可以通过await()和signal()等方法实现线程的等待和唤醒。 Semaphore的适用场景 Semaphore在以下场景中特别有用 控制对有限资源的并发访问如数据库连接池、线程池等。 限制同时执行某个操作的线程数量如限流和限制并发请求等。 在生产者-消费者模式中平衡生产者和消费者之间的速度差异。
总结
Semaphore是Java并发编程中一个强大的工具用于控制对共享资源的访问和实现线程之间的同步。与其他并发控制机制相比Semaphore具有灵活性和可扩展性。选择Semaphore还是其他机制取决于具体的需求和场景。在使用Semaphore时需要注意正确地获取和释放许可以避免死锁和资源争用等问题。