回顾
信号量
- 与锁的区别:信号量可以允许多个进程进入等待区。
- 进入等待区执行p操作,离开等待区执行v操作,唤醒wait的进程。
信号量的使用
可以作用在两个方面:1. 互斥 2.条件同步(调度约束-一个线程等待另一个线程的发生)
条件同步:线程b执行v操作后,才会让a执行p之后的操作。
信号量的实现
实现原理:
P:先将信号量-1,如果信号量小于0,将当前进程加入阻塞队列,阻塞住当前进程。
V:先将信号量+1,如果信号量小于或者等于0,说明有进程阻塞在当前信号量上,通知唤醒阻塞队列,FIFO。按照先来后到唤醒。
信号量的问题
相比于上章介绍的
锁在阻塞的时候有两种情况,1.忙等待(自旋,占用CPU)2.加入阻塞队列(sleep,不占用CPU)。
信号量在阻塞的时候只有一种情况:加入阻塞队列(sleep,不占用CPU)
管程(Monitor)
-
目的:分离互斥和条件同步的关注
-
定义:管程(monitor)只是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行,因此需要设置condition变量,让进入管程而无法继续执行的进程阻塞自己,JAVA同步互斥使用的管程。
如图:进入管程的线程是互斥的,所以需要一个队列entry queue,获得lock之后才能进入。进入管程以后,就可以执行管程所维护的一系列函数或者变量,如图中间部分。如果线程有冲突需要等待,释放锁,那么这时候就需要条件变量(condition)来控制,如图x、y两个等待队列,条件变量有两个操作wait,notify。 -
由两部分组成:
1、一个锁(Lock):指定临界区;
2、0或者多个条件变量(Condition):等待/通知信号量用于管理并发访问共享数据。
-
条件变量(Condition)的实现
等待操作:数目加一,添加到阻塞队列,
唤醒操作:如果等待的线程数大于一,再去唤醒线程。数目减一。
条件变量与信号量的区别:1. 信号量表示的信号量个数,condition表示的等待线程个数。2.信号量的PV操作是一定要出现的,就是加和减操作。而condition不一定,只有大于一才会往下执行。
问:sleep和wait的区别
答:wait放弃对象锁,sleep未放弃对象锁。因为通过管程中条件变量的wait的实现中可以看到,他在执行schedule前release了lock。这样才使得其他线程进入管程去执行。 -
管程实现生产者消费者
两个成员,锁和两个条件。count代表buffer数量。
生产和消费要互斥,所以头和尾加锁。
同步机制:buffer满,条件变量等待。
注意到wait传入了lock,wait中先执行了releaseLock,否则别的线程无法进入生产线程了,执行完以后再去acquire lock,如wait操作实现所示。 -
对于执行notify之后,管程中哪个线程先执行?是执行notify的那个线程还是被notify的线程呢?
左侧:唤醒线程等待被唤醒线程执行完之后再去执行。--实现容易,java属于此类。
右侧:唤醒操作后,唤醒线程先执行完。release后,被唤醒线程再去执行。--实现困难,需要更复杂机制。
由于两种机制不同,在设计生产消费的时候,条件语句也产生不同。
总结
信号量和管程相比于之前介绍的lock,更高一层。这两种机制都需要底层lock的支持。lock也需要硬件原子操作。
经典同步问题
- 读者写者问题
- 哲学家就餐问题