并发万能钥匙管程

并发万能钥匙管程

什么是管程

管程对应英文Monitor,英文直译为监视器,所谓管程指的是用来管理共享变量以及对共享变量的操作过程让其支持并发。

管程在发展过程中有三种管程模型,Hasen模型、Hoare模型和MESA模型,目前使用最多的是MESA模型,并且JAVA管程的实现也是参考MESA模型。

管程之所以能称作并发万能钥匙是因为并发的一切问题管程都有方案解决,例如并发的两大核心问题,互斥即同一个时刻只允许一个线程访问共享资源,同步指线程间如何通信和协作,管程都有答案。

并发问题之互斥

管程解决互斥问题很简单,就将不是线程安全的方法和变量全部封装起来,仅对外提供线程安全的方法,如下图所示,管程X将共享变量队列queue和入队操作enq,出队操作deq都封装起来,只能采用管程提供的入队操作enq,出队操作deq,保证线程安全操作。

并发问题之同步

同步并不是指不同线程间变量值同步而是指线程间的协作和通信。

场景引入

生活中的就医流程,再加入获取锁时的等待通知机制,构成如下流程。

  1. 患者挂号,排队等待挂号(等待获取互斥锁)。
  2. 被叫到号,患者找大夫就诊(获取互斥锁)。
  3. 就诊过程中可能需要会让患者做其他检查,同时叫下一位患者(线程不满足执行要求,本线程释放锁,其其它线程抢到互斥锁)。
  4. 患者做完其他检查重新复诊,等待叫号(线程执行满足要求,重新排队获取锁)。
  5. 当再次叫到自己的号,再次找大夫就诊(获取互斥锁)。

管程的同步原理也是类似的上诉流程,场景图如下。

那上图中的条件变量和条件变量等待队列有什么作用呢?其实这两个就是去解决同步问题的,如下例子。

假设存在阻塞队列Q里面没有元素,阻塞队列要求如果队列为空不允许出队,队列满了不允许入队。

线程T1请求出队,这里的阻塞队列不为空就称为条件变量,不满足条件进入等待队列等待,类似就医例子中的需要患者做其他检查比如验血,那么患者就去验血的队伍里面排队,这时候是允许其它线程进入管程内部的,同比患者去做验血医生是可以接待下一个患者的。

紧跟着线程T2请求入队,满足条件变量入队成功,这时线程T2是需要通知线程T1,现在条件变量已经满足要求,可以从等待队列中出来,这时T1从等待队列中出来并不是直接进入管程内部,而是进入入口等待队列,重新排队,就像患者验血完成后又需要重新排队取号,等待叫号的流程,将上诉思路代码化如下。

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.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    // 出队操作
    void deq(T x) {
        lock.lock();
        try {
            while (队列为空){
                // 出队操作等待
                notEmpty.wait();
            }
            // 出队操作省略
            notFull.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

当执行入队操作,队列已满那么入队的条件变量需要等待,直到队列不满所以调用notFull.await();

当执行出队操作,队列为空那么出队的条件变量需要等待,直到队列不空所以调用notEmpty.wait();

MESA编程范式

因为这里管程采用模型是MESA模型,所以这里wait需要遵循编程范式,如下

while(条件){
    wait();
}

如果不采用while而是改为if语句其实是满足语义要求的,但是这里存在一个虚假唤醒的问题,仅仅只当唤醒的此刻是满足条件变量的,但是线程需要回到入口等待队列等待,中间可能会被其它线程所篡改,所以这里才采用while做出判断。

在jdk1.8的文档中特别指出这一点

image-20220217184453721

总结

Java语言中对管程的实现是对MESA模型进行精简过后的,MESA中可以存在多个条件变量而JAVA的对管程的实现Synchronized却只支持一个,不过在SDK并发包中实现管程就支持多个条件变量了,不足的地方就是需要手动对并发代码块进行加锁解锁操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值