深入浅出多线程系列之十三:使用Wait和Pulse 来模拟一些同步构造

简介:

你可能在上篇文章中深入浅出多线程系列之十二:双向信号和竞赛 注意到了这个模式:两个Waiting 循环都要下面的构造:

lock (_locker)
{
        
while ( ! _flag) Monitor.Wait(_locker);
        _flag 
=   false ;
}

 

在这里_flag被另一线程设置为true。这是,从作用上讲,这里在模仿AutoResetEvent。如果我们将 _flag = false;去掉,那么我们就得到了一个基本的ManualResetEvent.

 

让我们使用WaitPulse来为ManualResetEvent完成剩余的代码吧。

复制代码
        readonly   object  _locker  =   new   object ();
        
bool  _signal;

        
void  WaitOne()
        {
            
lock  (_locker)
            {
                
while  ( ! _signal) Monitor.Wait(_locker);
            }
        }

        
void  Set()
        {
            
lock  (_locker) { _signal  =   true ; Monitor.PulseAll(_locker); }
        }

        
void  Reset() {  lock  (_locker) _signal  =   false ; }
复制代码

 

在这里使用PulseAll,是因为可能有很多阻塞的线程。

 如果在WaitOne方法中增加_signal=false就可以简单的模拟AutoResetEvent.例如:

复制代码
       void  WaitOne()
        {
            
lock  (_locker)
            {
                
while  ( ! _signal) Monitor.Wait(_locker);
                _signal 
=   false // 实现自动关闭功|能
            }
        }
复制代码

然后在Set方法中,将PulseAll修改为Pulse

Lock(_locker) {_signal = true; Monitor.Pulse(_locker);}

 

如果使用的是int类型的_signal 标志,那么我们可以得到一个最基本的Semaphore.

 

Waiting Queues and PulseAll

当多余一个线程在同一个对象上面等待的时候,一个 等待队列(waiting queue)” 就形成了。

每一次调用Pulse都会释放在等待队列头部的一个线程。下面的图形象的展示了这一点:

 

 

线程调用Monitor.Enter 进入ReadyQueue. 等待获取锁,成功获取锁后,如果正常的执行,那么之后会调用Monitor.Exit退出,

否则如果获取了锁之后发现需要等待其他的线程或者是其他阻塞条件,那么调用Wait方法,就进入了等待队列,

当等待的线程完成并调用Pulse后,处在WaitingQueue头部的线程就被 Pulse了,等待CPU调度 。之后再次进入Ready Queue,重新获取锁。

 

Countdown

借助WaitPulse,我们可以实现CountdownEvent的主要功能。例如:

复制代码
class  Countdown
    {
        
object  _locker  =   new   object ();
        
int  _value;  // 使用_value来计数
        
        
public  Countdown() { }
        
public  Countdown( int  initialCount) { _value  =  initialCount; }

        
public   void  Singnal() { AddCount( - 1 ); }  // 将计数减一

        
public   void  AddCount( int  amount)
        {
            
lock  (_locker)
            {
                _value 
+=  amount;  // 将计数增加或减少
                 if  (_value  <=   0 ) Monitor.PulseAll(_locker); // 如果value<=0,说明所有等待的任务都完成了。
            }
        }

        
public   void  Wait()
        {
            
lock  (_locker)
            {
                
// 只要计数 > 0 就等待。
                 while  (_value  >   0 )
                {
                    Monitor.Wait(_locker);
                }
            }
        }
}
 
复制代码

 

这和我们上次的代码几乎一致,只是这次我们的阻塞条件基于一个整型_value标志。

 






本文转自LoveJenny博客园博客,原文链接:http://www.cnblogs.com/LoveJenny/archive/2011/06/03/2060879.html,如需转载请自行联系原作者
目录
相关文章
|
14天前
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
Java多线程同步大揭秘:synchronized与Lock的终极对决!
51 5
|
14天前
|
安全 Java 开发者
Java多线程同步:synchronized与Lock的“爱恨情仇”!
Java多线程同步:synchronized与Lock的“爱恨情仇”!
75 5
|
14天前
|
Java 程序员
从0到1,手把手教你玩转Java多线程同步!
从0到1,手把手教你玩转Java多线程同步!
16 3
|
14天前
|
Java 测试技术
Java多线程同步实战:从synchronized到Lock的进化之路!
Java多线程同步实战:从synchronized到Lock的进化之路!
77 1
|
20天前
|
安全 Java 调度
|
18天前
|
存储 Java 开发者
HashMap线程安全问题大揭秘:ConcurrentHashMap、自定义同步,一文让你彻底解锁!
【8月更文挑战第24天】HashMap是Java集合框架中不可或缺的一部分,以其高效的键值对存储和快速访问能力广受开发者欢迎。本文深入探讨了HashMap在JDK 1.8后的底层结构——数组+链表+红黑树混合模式,这种设计既利用了数组的快速定位优势,又通过链表和红黑树有效解决了哈希冲突问题。数组作为基石,每个元素包含一个Node节点,通过next指针形成链表;当链表长度过长时,采用红黑树进行优化,显著提升性能。此外,还介绍了HashMap的扩容机制,确保即使在数据量增大时也能保持高效运作。通过示例代码展示如何使用HashMap进行基本操作,帮助理解其实现原理及应用场景。
23 1
|
1月前
|
Java 调度 开发者
Java并发编程:解锁多线程同步的奥秘
在Java的世界里,并发编程是提升应用性能的关键所在。本文将深入浅出地探讨Java中的并发工具和同步机制,带领读者从基础到进阶,逐步掌握多线程编程的核心技巧。通过实例演示,我们将一起探索如何在多线程环境下保持数据的一致性,以及如何有效利用线程池来管理资源。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你对Java并发编程有更深入的理解和应用。
|
17天前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
26 0
|
2月前
|
安全 Java 程序员
Java 并发编程:解锁多线程同步的奥秘
【7月更文挑战第30天】在Java的世界里,并发编程是一块充满挑战的领域。它如同一位严苛的导师,要求我们深入理解其运作机制,才能驾驭多线程的力量。本文将带你探索Java并发编程的核心概念,包括线程同步与通信、锁机制、以及并发集合的使用。我们将通过实例代码,揭示如何在多线程环境中保持数据的一致性和完整性,确保你的应用程序既高效又稳定。准备好了吗?让我们一同踏上这段解锁Java并发之谜的旅程。
33 5
【多线程面试题九】、说一说sleep()和wait()的区别
sleep()和wait()的主要区别在于sleep()是Thread类的静态方法,可以在任何地方使用且不会释放锁;而wait()是Object类的方法,只能在同步方法或同步代码块中使用,并会释放锁直到相应线程通过notify()/notifyAll()重新获取锁。