wait函数_java 中的wait()和notify()

看到MOS书上讲到c, java和进程/线程间通信那一套,讲的还是比学校书上讲的清楚的。随便写一写。

java 中的wait()和notify()是实现condition variables的具体方法。教科书上的condition variables模型需要指定具体的变量,比如在生产者和消费者模型中的condition full, empty,分别表示由于buffer已满而造成生产者堵塞,buffer已空而造成消费者堵塞。因此在生产者的代码中当判断到buffer已满则执行wait(full),在消费者的代码中当判断到buffer已空则执行wait(empty)。当然,在生产者生产出第一个产品的时候就应该signal(empty),给堵塞在empty这个原因上的所有线程/进程随机选择一位进入就绪队列。同理,消费者消费了第一个产品就应该signal(full)。下面是用condition variables解决生产者消费者问题的伪代码,condition variables和所有函数都放在了monitor中,即生产/消费函数在任何一个时刻有且只有一个线程/进程在执行。(出自Modern Operating System 4th Edition, page 141)

ee250f6f5e141f5848c4c83e3b505743.png

把他们放在moditor中(或者两个函数都用mutex包裹起来是很重要的)。

因为线程之间常用于应用内部的相互合作,比如WEB服务器中可能一票线程用来接受并解析request,一票进程用来提供服务返回。有些OS并不会对线程采用固定时间片调度,线程只能自己选择放弃CPU让别的线程执行(比如POXIS中对系统调用封装的函数中就有pthread_yield),这个没有问题。但是进程之间默认是相互竞争的关系,它们都需要尽可能多的CPU执行时间,因此用时间片调度保证公平。重点是如果在wait(full)操作中如果wait还没执行完毕,进程还没被堵塞,这时时间片到期,消费者开始执行,消费一个产品使得buffer有了空间并signal(full),但生产者还没有堵塞呀,因此信号被浪费掉了。时间片再轮转到生产者,执行wait剩下的指令,并被堵塞。消费者一直消费到空也被堵塞,全部完蛋。

因此上述过程问题出在两个地方,一是信号被浪费掉了,如果能把信号累积起来多好啊,所以有了semaphores。二是进程在执行到一半被调度了,生产者还没睡下去,消费者不能signal啊,所以我们干脆把生产者函数和消费者函数放到monitor中,同时只允许一个进程进入monitor(或者两个函数都用mutex包裹起来也可以的)。

MOS作者强调monitor是语言机制,在编译器那里实现的进程/线程同步,因此c没有java有。java用synchronized实现类似monitor的思想,用java实现生产者消费者代码如下:

0c305b0fa7e2c492667f0d69576b21ad.png

我们要知道,synchronized修饰整个方法,它的作用对象是调用该方法的那一个具体对象(如果是静态方法,那就是该类)。现在有两个线程p和c,一个our_monitor类对象mon,因此p在调用mon的同步方法insert的时候拿到的是整个mon的锁,另一个同步方法mon.remove()是不能执行滴~所以这样就实现了monitor的机制了。

回到标题,java的wait和notify继承自object类,并且必须要在一个synchronized代码模块内执行,因为wait的意思是阻塞当前线程/进程并释放所有的锁。所以在生产者调用的mon.insert中执行wait,释放了synchronized锁住的mon对象,消费者线程开始执行,当消费者执行到notify时,注意notify会随机释放当前对象中被堵塞的线程/进程,一看正好是消费者线程。这样的机制保证了虽然wait和notify没有像condition variables一样指明在什么条件下堵塞(full, empty),但一概堵塞在当前对象下,上述代码依然正确。

接下来我思考如果有多个生产者和多个消费者会怎么样呢?首先synchronized依然能保证只有一个线程在调用mon的方法,当buffer满时,调用mon.insert的生产者们都会被堵塞。只有当消费者调用mon.remove时才会在最后释放一个signal,唤醒一个生产者。但是仍然有多个生产者被堵塞着呀,明显不够高效。

因此这里有两种方案:1)把notify()改成notifyAll(),所有生产者全部唤醒,这样就能保证当产品的数量降到N-1以下时,所有生产者全部都是醒着的。同理在产品数量>=1时,所有消费者也都是醒着的。缺点是当生产者和消费者数量差距悬殊时,会存在饥饿现象。2)消费者每消费一个,notify()一次,生产者也同理。这样的好处是保持了消费者和生产者数量的动态平衡,二者活跃线程数量是逐渐变化的,运行效率会高很多。

以上就是java的synchronized和wait(), notify()与进程/线程同步中的monitor, mutex, semaphore 的关系。都是自己理解写的,欢迎指正错误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值