管程-synchronized
什么是管程?
所谓管程,指的是管理共享变量以及对共享变量的操作过程,让他们支持并发。用java来说,管理一个类的成员变量和成员方法,使之成为线程安全的类。
synchronized关键字和wait(),notify()和notifyall()都是管程的组成部分。
管程模型-MESA 模型
在并发编程领域,有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信、协作。看管程模型MESA是如何解决并发问题的。
- 解决互斥问题:
当多个线程同时试图进入管程内部时,只允许一个线程进入,其他线程则在入口等待队列中等待。 - 解决同步问题:
该管程模型中还引入条件变量的概念,而且每个条件变量都有一个等待队列。当线程执行期间满足条件变量A,释放锁对象,进入相应的条件变量等待队列中,等待被唤醒,再次添加到入口等待队列中。
管程模型的区别
在管程发展史上,出现三种不同的管程模型,分别是:Hasen模型,Hoare模型,MESA模型,java的管程参考的就是这个模型。
管程要求同一时刻只允许一个线程执行,那三个模型的区别就是当T1唤醒(notify)T2等待的线程(wait),那究竟是T1执行呢还是T2执行?
- Hasen 模型,notify()放在代码的最后面,这样在唤醒T2线程后,T1也就执行完了,然后T2再执行,这样就能保证同一时刻只有一个线程运行。
- Hoare模型,T1唤醒了T2,T1陷入阻塞,T2马上执行,T2执行完,再唤醒T1,这样也能保证同一时刻只有一个线程运行。相比Hasen模型,T1多了一个阻塞唤醒的操作。
- MESA模型,结合上面的MESA模型图,T1唤醒T2,并不马上执行T2,而是把T2从条件变量等待队列中放到入口等待队列中,T1继续执行。
好处:1.motify不用放到代码最后,2.也没有多余的唤醒阻塞情况
坏处:当T2在次执行时,可能曾经满足的条件,现在不满足了,所以要以循环方式检验条件变量。
注意:因为再次放到入口等待队列中,期间条件变量是不确定的,需要再次判断条件变量是否满足,所以wait()方法需要
while(条件不满足) {
wait();
}
简易版 MESA 模型synchronized
JAVA参考MESA模型,java内置管程模型synchronized对MESA模型做了精简。
MESA模型条件变量可以有多个,synchronized条件变量只有一个,请看下图
如果此时你还是优点不懂一个条件变量个两个条件变量的区别,上代码,来展示一下
- 使用java SDK并发包中LOCK实现多个条件变量
public class BlockedQueue<T>{
final Lock lock = new ReentrantLock();
// 条件变量:队列不满
final Condition notFull = lock.newCondition();
// 条件变量:队列不空
final Condition notEmpty = lock.newCondition();
// 入队
void enq(T x) {
lock.lock();
try {
while (队列已满){
notFull.await(); // 等待队列不满
}
// 省略入队操作...
notEmpty.signal(); //入队后,通知可出队
}finally {
lock.unlock();
}
}
// 出队
void deq(){
lock.lock();
try {
while (队列已空){
notEmpty.await(); // 等待队列不空
}
// 省略出队操作..
notFull.signal(); //出队后,通知可入队
}finally {
lock.unlock();
}
}
}
2.使用synchronized实现一个条件变量
class Allocator {
private String demo;
// 经典写法
while(demo==null){
try{
wait();
}catch(Exception e){
}
}
}
// 归还资源
synchronized void free(
if(demo!=null){
notifyAll();
}
}
其实:总结来说MESA管程,
- 就是管程保证只有一个线程在方法中执行,其他等待线程放入入口等待队列中,
- 添加条件变量,可以判断进入方法体中,当前状态是否满足,满足就往下执行,不满足,可以wait(),
- 放入条件变量等待队列中,若在某时刻该条件变量满足了,就notify,把该线程从条件变量等待队列中放入入口等待队列中,等待本次线程执行结束,从入口等待队列中取出一个线程从上次等待的代码继续放下执行,
- 因为是从上次执行的代码出开始往下执行,同时因为是等待线程结束可能条件变量又会发生变化,所以wait需要while循环。