设为首页 收藏本站
查看: 585|回复: 0

[经验分享] java与python多线程wait,notify操作比较

[复制链接]

尚未签到

发表于 2017-5-4 09:59:24 | 显示全部楼层 |阅读模式
  wait 和notify的应用场景
  在学习wait,notify之前首先需要解释java中wait()和notify()的应用场景。wait和notify提供了对多个线程之间的等待和通知操作。例如抓取站外多张图片通常会通过多个thread同时进行,但主线程需要等到这批数据返回的结果。
  多线程操作通常都有提交者(submiter)和执行者(executor),java通过concurrent包提供的Executors提供了很好的支持,如果不通过wait和notify,只能通过轮循来实现,实际上是很低效的:
  看看之前我们网站的fetch实现方式:

def fetch(self, url):
for i in range(2):
title, images = build_fetch(url).fetch(url)
if images:
break
if images:
images = images[:150]
self._finished = False
self._total = len(images)
current = 0
while current < self._total:
self.pool.queueTask(self.check_image, images[current:current + self._per_check], self.collect_image)
current = current + self._per_check
from time import sleep
# 等待抓取完毕
while not self._finished:
sleep(0.05)
pass
return title, self._urls

  java
  首先可以这样理解,每个object实际上自身和一个monitor(锁)关联,object.wait(timeout) :使当前线程放弃object的锁并等待,除非其它线程调用了object.notify()或者object.notifyAll(),或者使等待线程中断,或者等待了timeout时间。
  object.notify():随机唤醒一个等待在object的线程 ,该线程和其他活动线程一起争夺object的锁。
  object.notifyAll():唤醒所有等待在object的线程 ,线程和其他活动线程一起争夺object的锁。
   
  根据 java api doc ,使用wait,notify注意事项:
  1. 调用线程必须已经获得了object的锁,即在synchronized方法或者synchronized(object){}语句块中调用。
  2. 调用线程被唤醒后实际上并不会立即执行后续操作,它要先和其它活动线程竞争获得当前对象的锁,得到对象锁后才能接着执行wait后代码。
  下面是一个例子:

public class Tester3 {
public synchronized void take() {
System.out.println("take");
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void put() {
System.out.println("put");
}
/**
* @param args
*/
public static void main(String[] args) {
final Tester3 tester = new Tester3();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("take begin");
tester.take();
System.out.println("take end");
}
});
t.start();
t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("put begin");
tester.put();
System.out.println("put end");
}
});
t.start();
}
}

  输出(put必须等到get释放锁之后才能被执行):
  take begin
put begin
take
take end
put
put end
  python
  python对象没有隐式的和一个锁关联,且python中的 wait,notify是由python语言自身利用锁(Lock)实现,实现类为Condition,但是概念思想上是和java保留一致,如果要模拟 java的话,只需创建python对象时,显式将一个Condition实例赋给创建对象的一个成员属性,那么可以对应java中的doc来看一下 python的实现:
   
  threading.py Condition类:
   
  1。wait,notify必须在获得当前对象锁的前提下:

def wait(self, timeout=None):
if not self._is_owned():
raise RuntimeError("cannot wait on un-aquired lock")
.......

def notify(self, n=1):
if not self._is_owned():
raise RuntimeError("cannot notify on un-aquired lock")
.......
  可见在wait,notify时都要进行检查,其中self._is_owned()正是判断调用线程是否获得了Condition的内置锁,也即java中对象monitor的概念。
  2.wait调用后会使当前线程放弃已经获得对象锁:

def wait(self, timeout=None):
.....
saved_state = self._release_save()
   
  其中 self._release_save正是进行了放弃Condition内置锁的操作,也对应着java先放弃对象monitor的概念

   
  3.wait 使当前线程等待的实现
  java doc说明:将当前线程加入object的waitset,然后等待。
  python实现为:当前线程在一个新建锁上等待,把该锁加入到condition的等待数组中,线程等待锁的release

def wait(self, timeout=None):
...
#新建一把锁
waiter = _allocate_lock()
#现获得一次,后面再获得就阻测
waiter.acquire()
#记录等待
self.__waiters.append(waiter)
.....
if timeout is None:
#在该锁上等待
waiter.acquire()
if __debug__:
self._note("%s.wait(): got it", self)
   
  4.notify唤醒等待线程实现
   
  同java不同,java notify唤醒的线程不能确定,而python则能确定,一定是第一个调用wait的线程被唤醒,即为先进先出的队列结构。

  对于python为:release __waiters等待数组的第一个锁,对应的等待线程即可重新开始在wait函数内运行:

def notify(self, n=1):
....
waiters = __waiters[:n]
for waiter in waiters:
#锁释放,意味着等待锁的对应线程可是从wait函数运行
waiter.release()
try:
__waiters.remove(waiter)
except ValueError:
pass
   
  5.唤醒线程和其他活动线程争夺对象锁
   
  唤醒线程并不是立刻从wait()返回开始它的实际操作,而是要先争夺conditon的内置锁,即java的object monitor:

def wait(self, timeout=None):
#等待在新建锁上
if timeout is None:
waiter.acquire()
#新建锁释放了,但是要先获得condition内置锁才能返回
self._acquire_restore(saved_state)
   
  6.wait的超时处理与notifyAll 略
  

  实例:
  分别用java与python实现了经典的生产者与消费者模型
  原文:http://yiminghe.iteye.com/blog/673379

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-372847-1-1.html 上篇帖子: 正则表达式使用学习(C++、Qt、Python) 下篇帖子: 《简明python教程 》总结(二)-- 字符串、对象、控制流
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表