`
stone2oo6
  • 浏览: 25124 次
社区版块
存档分类
最新评论

线程笔记(wait,notify,notifyAll)

阅读更多

我们先看看代码片段,有点类似生产者-消费者模型中的生产者的逻辑代码。

  public synchronized void push(Object obj){  
        while( condition ){  
            try{  
                this.wait(); //等待,直到有数据出栈  
            }catch(InterruptedException e){  
            }  
        } 
        //do something
        stack.push(obj);  
        this.notify(); //通知其它线程把数据出栈  
    }
注意第4行,代码执行到这里会发生什么状态?
push方法的签名中有synchronized修饰,所以此方法具有排他性,锁(资源)为自身实例对象,也就是说同时同一个对象的这个方法只能被一个线程所执行,那么当执行到第4行时会发生什么呢? XX一紧,线程在释放排他锁资源之前,自己把自己给阻塞了? 这样岂不是死锁了?
翻翻JDK1.6手册,便恍然大悟:
wait方法共有3个重载函数:
  1. public final void wait(long timeout) throws InterruptedException
  2. public final void wait(long timeout, int nanos) throws InterruptedException
  3. public final void wait() throws InterruptedException
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:
  • 其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。
  • 其他某个线程调用此对象的 notifyAll 方法。
  • 其他某个线程中断线程 T
  • 大约已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。
然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态,这就是调用 wait 方法时的情况。然后,线程 Twait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调用 wait 方法时的情况完全相同。

简而言之:
  1. wait方法的作用释放已持有的锁,并进入休眠状态;当被唤醒或是等待超时,当前的线程又会重新加入到锁等待队列中,一旦获取了对该对象的控制权,则会从原来的地方继续执行;如果线程被中断,则会抛出InterruptedException异常。
  2. notify方法唤醒等待队列中的第一个线程并把它移入锁申请队列中
  3. notifyAll方法唤醒等待队列中的所有的线程并把它们移入锁申请队列中
  4. wait,notify,notifyAll必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用范围内,也就是出现在用synchronized修饰的方法或代码块中.
  5. 在更多线程的情况下(e.g. 拿消费者/生产者模型来讲,可能存在多个消费者和多个生产者),线程被唤醒后依然需要对条件进行测试,如果不满足该条件,则继续等待。换句话说,等待应总是发生在循环中。如上述代码示例(2-7循环体)
附上完整的示例代码:
public class ThreadDemo {
	private int x = 0;

	public synchronized void add() {
		while (x >= 2) {
			try {
				this.wait();
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

		x++;
		System.out.println("add:" + x);
		this.notifyAll();
	}

	public synchronized void sub() {
		int y = 1;
		while (x < 2) {
			System.out.println("sub:while repeat = " + y);
			y++;
			try {
				this.wait(); //this.wait(1);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}

		x--;
		System.out.println("sub:" + x);
		this.notifyAll();
	}

	public static void main(String[] args) throws Throwable {
		final ThreadDemo tt = new ThreadDemo();
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 100; i++) {
					tt.add();
					try {
						Thread.sleep((int) (Math.random() * 1000));
					} catch (InterruptedException e) {
					}
				}

			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 100; i++) {
					tt.sub();
					try {
						Thread.sleep((int) (Math.random() * 1000));
					} catch (InterruptedException e) {
					}
				}

			}
		}).start();
	}
}
  在代码第25行处,可以试着设置下等待超时时间,然后看看测试结果。
0
2
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics