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时,需要注意正确地获取和释放许可,以避免死锁和资源争用等问题。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
#define _GNU_SOURCE #include &quot;sched.h&quot; #include&lt;sys/types.h&gt; #include&lt;sys/syscall.h&gt; #include&lt;unistd.h&gt; #include &lt;pthread.h&gt; #include &quot;stdio.h&quot; #include &quot;stdlib.h&quot; #include &quot;semaphore.h&quot; #include &quot;sys/wait.h&quot; #include &quot;string.h&quot; int producer(void * args); int consumer(void * args); pthread_mutex_t mutex; sem_t product; sem_t warehouse; char buffer[8][4]; int bp=0; int main(int argc,char** argv){ pthread_mutex_init(&amp;mutex,NULL);//初始化 sem_init(&amp;product,0,0); sem_init(&amp;warehouse,0,8); int clone_flag,arg,retval; char *stack; //clone_flag=CLONE_SIGHAND|CLONE_VFORK //clone_flag=CLONE_VM|CLONE_FILES|CLONE_FS|CLONE_SIGHAND; clone_flag=CLONE_VM|CLONE_SIGHAND|CLONE_FS| CLONE_FILES; //printf(&quot;clone_flag=%d\n&quot;,clone_flag); int i; for(i=0;i&lt;2;i++){ //创建四个线程 arg = i; //printf(&quot;arg=%d\n&quot;,*(arg)); stack =(char*)malloc(4096); retval=clone(producer,&amp;(stack[4095]),clone_flag,(void*)&amp;arg); //printf(&quot;retval=%d\n&quot;,retval); stack=(char*)malloc(4096); retval=clone(consumer,&amp;(stack[4095]),clone_flag,(void*)&amp;arg); //printf(&quot;retval=%d\n\n&quot;,retval); usleep(1); } exit(1); } int producer(void *args){ int id = *((int*)args); int i; for(i=0;i&lt;10;i++){ sleep(i+1); //表现线程速度差别 sem_wait(&amp;warehouse); pthread_mutex_lock(&amp;mutex); if(id==0) strcpy(buffer[bp],&quot;aaa/0&quot;); else strcpy(buffer[bp],&quot;bbb/0&quot;); bp++; printf(&quot;producer %d produce %s in %d\n&quot;,id,buffer[bp-1],bp-1); pthread_mutex_unlock(&amp;mutex); sem_post(&amp;product); } printf(&quot;producer %d is over!\n&quot;,id); exit(id); } int consumer(void *args){ int id = *((int*)args); int i; for(i=0;i&lt;10;i++) { sleep(10-i); //表现线程速度差别 sem_wait(&amp;product); pthread_mutex_lock(&amp;mutex); bp--; printf(&quot;consumer %d get %s in %d\n&quot;,id,buffer[bp],bp+1); strcpy(buffer[bp],&quot;zzz\0&quot;); pthread_mutex_unlock(&amp;mutex); sem_post(&amp;warehouse); } printf(&quot;consumer %d is over!\n&quot;,id); exit(id); }
06-01
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狠情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值