`
san_yun
  • 浏览: 2594955 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

java与python多线程wait,notify操作比较

 
阅读更多

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics