Semaphone应用&源码分析(二)

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()等方法实现线程的等待和唤醒。

  1. Semaphore的适用场景: Semaphore在以下场景中特别有用:

  • 控制对有限资源的并发访问,如数据库连接池、线程池等。

  • 限制同时执行某个操作的线程数量,如限流和限制并发请求等。

  • 在生产者-消费者模式中平衡生产者和消费者之间的速度差异。

总结:

Semaphore是Java并发编程中一个强大的工具,用于控制对共享资源的访问和实现线程之间的同步。与其他并发控制机制相比,Semaphore具有灵活性和可扩展性。选择Semaphore还是其他机制取决于具体的需求和场景。在使用Semaphore时,需要注意正确地获取和释放许可,以避免死锁和资源争用等问题。

相关推荐

  1. Semaphone应用&分析

    2023-12-24 06:50:06       61 阅读
  2. **FutureTask应用&分析**(

    2023-12-24 06:50:06       55 阅读
  3. 【Spark分析】Spark的RPC通信-初稿

    2023-12-24 06:50:06       50 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-24 06:50:06       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-24 06:50:06       100 阅读
  3. 在Django里面运行非项目文件

    2023-12-24 06:50:06       82 阅读
  4. Python语言-面向对象

    2023-12-24 06:50:06       91 阅读

热门阅读

  1. 音视频转码

    2023-12-24 06:50:06       58 阅读
  2. obs video-scaler-ffmpeg.c 源码讲解

    2023-12-24 06:50:06       45 阅读
  3. 微信小程序生成一个天气查询的小程序

    2023-12-24 06:50:06       64 阅读
  4. C语言实现对数组去重算法详解

    2023-12-24 06:50:06       65 阅读
  5. HarmonyOS和OpenHarmony的区别

    2023-12-24 06:50:06       64 阅读
  6. OpenCV技巧: 图像孔洞填充的方法与实现

    2023-12-24 06:50:06       56 阅读
  7. 云卷云舒:云原生业务应用成熟度模型

    2023-12-24 06:50:06       71 阅读
  8. facebook广告投放对落地页的要求

    2023-12-24 06:50:06       66 阅读
  9. POP3协议详解

    2023-12-24 06:50:06       53 阅读