JUC怎么模拟AND型信号量(and,juc,编程语言)

时间:2024-05-08 19:23:06 作者 : 石家庄SEO 分类 : 编程语言
  • TAG :

    JUC%E6%80%8E%E4%B9%88%E6%A8%A1%E6%8B%9FAND%E5%9E%8B%E4%BF%A1%E5%8F%B7%E9%87%8F

接下来,请跟着小编一起来学习吧!/p><p><strong>1.一个错误示例</strong></p><p>在这里,首先解释一下,为了满足线程申请信号量不成功后将进程阻塞,并插入到对应的队列中,所以使用了ReentrantLock+Condition来实现Swait方法。废话不多说,直接上代码:</p><pre>//数据定义staticLocklock=newReentrantLock();staticConditioncondition1=lock.newCondition();staticConditioncondition2=lock.newCondition();publicstaticvoidSwait(Stringid,Semaphores1,Semaphores2)throwsInterruptedException{lock.tryLock(1,TimeUnit.SECONDS);log.info(&quot;当前的两个信号量的状态:【{},{}】&quot;,s1.availablePermits(),s2.availablePermits());//availablePermits可获取到信号量中还剩余的值if(s1.availablePermits()&lt;1||s2.availablePermits()&lt;1){if(s1.availablePermits()&lt;1){log.info(&quot;线程【{}】被挂起到信号量【{}】中&quot;,id,s1);//阻塞,并插入到condition1的阻塞队列中condition1.await();}else{log.info(&quot;线程【{}】被挂起到信号量【{}】中&quot;,id,s2);//阻塞,并插入到condition2的阻塞队列中condition2.await();}log.info(&quot;被挂起的线程【{}】被唤醒执行。&quot;,id);}else{log.info(&quot;为线程【{}】分配资源!&quot;,id);s1.acquire();s2.acquire();}lock.unlock();}publicstaticvoidSsignal(Semaphores1,Semaphores2)throwsInterruptedException{log.info(&quot;线程【{}】执行了释放资源&quot;,id);lock.tryLock(1,TimeUnit.SECONDS);s1.release();s2.release();//唤醒等待队列中的线程condition.signal();lock.unlock();}</pre><p>大家仔细看上面的代码,这个也是我刚开始写的代码,第一眼看似乎是没什么问题,但是里面隐藏着一个坑,在Swait方法中,调用condition1.await(),此时线程被阻塞在这一行中,但是当被别的线程(调用Ssignal)唤醒时,在被阻塞的下一行开始继续执行,但是在后续的代码里,是没有去申请信号量的,而是直接就Swait成功了,这样在执行Ssignal时就会导致信号量凭空的增加了,也就无法正确的表征系统中的资源数量了。</p><p><strong>2.一个简单的示例</strong></p><p>下面我们就对代码进行优化,大家可以回顾一下AND型信号量,当其因为资源不足时,需要将线程插入到第一个无法满足条件(即Si&lt;1)的信号量对应的等待队列中,并且将程序计数器放置到Swait操作的开始处,所以我们对Swait代码进行修改如下:</p><pre>publicstaticvoidSwait(Stringid,Semaphores1,Semaphores2)throwsInterruptedException{lock.tryLock(1,TimeUnit.SECONDS);log.info(&quot;当前的两个信号量的状态:【{},{}】&quot;,s1.availablePermits(),s2.availablePermits());//如果申请不到,就挂起线程,并将线程插入到condition的队列中while(s1.availablePermits()&lt;1||s2.availablePermits()&lt;1){if(s1.availablePermits()&lt;1){log.info(&quot;线程【{}】被挂起到信号量【{}】中&quot;,id,s1);condition1.await();}else{log.info(&quot;线程【{}】被挂起到信号量【{}】中&quot;,id,s2);condition2.await();}log.info(&quot;被挂起的线程【{}】被唤醒执行。&quot;,id);}log.info(&quot;为线程【{}】分配资源!&quot;,id);s1.acquire();s2.acquire();lock.unlock();}</pre><p>在上面的代码中,我们将请求的资源放到一个循环条件中,以满足将程序计数器放置到Swait操作的开始处,在每次被唤醒后都要重新判断资源是否足够,如果足够才跳出循环,否则就再次自我阻塞。</p><p><strong>3.一个可以同时申请N个的Swait操作</strong></p><p>如果你知道了信号量的种类数(系统中的资源类型),其实上面的代码已经可以满足一定的需要了,只需要我们将所有的信号量写入到参数列表中即可。但是对于致力于代码的复用,这里就有些差强人意了,因此我们再次对代码进行改进,代码如下所示:</p><pre>publicstaticvoidSwait(Stringid,Semaphore...list)throwsInterruptedException{lock.lock();//如果资源不足,就挂起线程,并将线程插入到condition的队列中while(true){intcount=0;//循环判断参数列表中信号量的可用值for(Semaphoresemaphore:list){if(semaphore.availablePermits()&gt;0){count++;}}//如果资源都满足,则跳出循环,进行资源分配if(count==list.length){break;}log.info(&quot;线程【{}】被挂起-----&quot;,id);//将当前线程阻塞condition1.await();log.info(&quot;被挂起的线程【{}】被唤醒执行。&quot;,id);}log.info(&quot;为线程【{}】分配资源!&quot;,id);//分配资源for(Semaphoresemaphore:list){semaphore.acquire();}lock.unlock();}publicstaticvoidSsignal(Stringid,Semaphore...list)throwsInterruptedException{log.info(&quot;线程【{}】执行了释放资源&quot;,id);lock.tryLock(1,TimeUnit.SECONDS);//循环释放信号量for(Semaphoresemaphore:list){semaphore.release();}//唤醒等待队列中的线程condition.signal();lock.unlock();}</pre><p>为此,我们将方法中的信号量列表改为可变的参数列表,这样在传参的时候就可以方便的进行了,但是也会存才一些问题,比如无法约束“借出”与“归还”的信号量的数量是否一致。并且因为信号量的数量不定,所以无法为每个信号量新建一个条件变量(Condition),因此在上面的代码中所有的信号量公用一个条件变量,所有阻塞的线程都插入在其阻塞队列中。</p><p><strong>4.一个完整的例子</strong></p><p>这里我们使用一个经典的进程同步问题来演示我们使用Java模拟的AND型信号量,在这里,我们采用生产者&amp;ndash;消费者问题来演示,完整的代码如下:</p><pre>//用来保证互斥的访问临界区(缓存区)staticfinalSemaphoremutex=newSemaphore(1);//缓冲区,最大容量为50staticList&lt;Integer&gt;buffer=newArrayList&lt;&gt;();//缓冲区中还可放入的消息数量staticfinalSemaphoreempty=newSemaphore(50);//缓冲区中的消息数量staticfinalSemaphorefull=newSemaphore(0);//可重入锁和条件变量staticLocklock=newReentrantLock();staticConditioncondition=lock.newCondition();//用与辅助的简单的生成消息staticIntegercount=0;//生产者staticclassProducerextendsThread{Producer(Stringname){super.setName(name);}@Overridepublicvoidrun(){do{try{Swait(this.getName(),mutex,empty);log.info(&quot;生产了一条消息:【{}】&quot;,count);buffer.add(count++);Thread.sleep(1000);Ssignal(this.getName(),mutex,full);}catch(InterruptedExceptione){log.error(&quot;生产消息时产生异常!&quot;);}}while(true);}}//消费者staticclassConsumerextendsThread{Consumer(Stringname){super.setName(name);}@Overridepublicvoidrun(){do{try{Swait(this.getName(),mutex,full);log.info(&quot;消费了一条消息:【{}】&quot;,buffer.remove(0));Thread.sleep(1000);Ssignal(this.getName(),mutex,empty);}catch(InterruptedExceptione){log.error(&quot;消费消息时产生异常!&quot;);}}while(true);}}publicstaticvoidSwait(Stringid,Semaphore...list)throwsInterruptedException{lock.lock();//如果资源不足,就挂起线程,并将线程插入到condition的队列中while(true){intcount=0;for(Semaphoresemaphore:list){if(semaphore.availablePermits()&gt;0){count++;}}if(count==list.length){break;}log.info(&quot;线程【{}】被挂起&quot;,id);condition.await();log.info(&quot;被挂起的线程【{}】被唤醒执行。&quot;,id);}log.info(&quot;为线程【{}】分配资源!&quot;,id);for(Semaphoresemaphore:list){semaphore.acquire();}lock.unlock();}publicstaticvoidSsignal(Stringid,Semaphore...list)throwsInterruptedException{log.info(&quot;线程【{}】执行了释放资源&quot;,id);lock.tryLock(1,TimeUnit.SECONDS);for(Semaphoresemaphore:list){semaphore.release();}//唤醒等待队列中的一个线程condition.signal();lock.unlock();}publicstaticvoidmain(String[]args){Producerp1=newProducer(&quot;p1&quot;);Consumerc1=newConsumer(&quot;c1&quot;);p1.start();c1.start();}</pre

上面代码都是可以直接执行的,如果不需要使用参数列表,可以将上面的Swait方法进行替换即可(记得创建对应的条件变量)。

本文:JUC怎么模拟AND型信号量的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:如何恢复SQLSERVER的master系统库下一篇:

8 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18