Java Object中wait()和notify()小议

来源:互联网 发布:网络种地 编辑:程序博客网 时间:2024/06/05 13:03

最近在学习Java中线程同步和线程通信问题,发现在Java基础类Object中有两个线程相关方法wait()和notify(),于是就做了点小的实验,下面就是实验的内容。

在介绍实验之前我们要弄清楚synchronized关键字的真正语义。

synchronized关键字是用来声明在执行某个代码块时,执行该代码块的线程必须获得某个特定对象的对象监视器(java 官方称其为moniter,民间传说称其为锁,下面我一概简称监视器),而一个对象的监视器只有一个。这句话什么意思呢,我们来看个例子代码。

StringBuffer sb = new StringBuffer();

synchronized(sb){//代码段1}

这段代码就是说当某个线程要执行代码段1时,该线程必须先获取对象sb的监视器,同时该线程将会持有该监视器一直到代码段1执行完毕才会将其释放。我们知道,一个对象的监视器只有一个,如果该对象的监视器被线程线程A持有了,那么任何其他线程在执行形如上面的代码时,也就是说在执行需要获取监视器的代码块时,都会被阻塞,因为他们要等待线程A释放掉监视器,也就是说等待线程A把同步代码块执行结束,他们才有可能获取处理器。

在方法前加synchronized其实就相当于

public void functionName(){

synchronized(this){

//方法执行代码

}

}

下面我们来看段代码

一般我们写线程等待会写成这样:

synchronized(obj){

//调用obj的方法

Thread.sleep(10000);

//调用obj的方法

}

在上面的代码中我调用了sleep方法是线程休眠的10秒,通过上面对synchronized的解释,我们知道如果有另一个线程要获取obj的监视器以执行同步代码块是不行的,虽然休眠的线程没有在执行,但是它却持有着obj的监视器,因此其他任何需要obj监视器才能执行的代码都必须等待。

wait()的作用就是使线程阻塞并且会暂时释放掉对象的监视器,notify()方法则是通知那些暂时释放监视器的线程回收监视器(注意这里只是通知,并不是立即,具体什么时候回收后面会解释)。

代码段2

synchronized(obj){

//调用obj的方法1

obj.wait()

//调用obj的方法2

}

代码段3

synchronized(obj){

//调用obj中的某些方法

obj.notify()

}

上面两段代码的执行都需要线程获取obj的监视器,而在代码段2中的wait将会阻塞当前线程,有就是说当调用了obj的方法1之后线程不会继续掉用obj的方法2而是将线程转为阻塞状态。按照我上面对synchronized的解释,当线程没有将代码段2中的代码全部执行完,其他线程是没有资格去执行代码段3的,但是wait()却可以让代码段3得到执行,因为wait()将会暂时释放掉obj的监视器,那么就是说代码段3就能够获取obj的监视器了,换言之代码段3就可以执行了。

在代码段3中我们调用了obj中的一些方法,最后我们调用了obj.notify()方法,它是干嘛的呢?现在我们假设执行代码段2的线程叫线程B,执行代码段3的线程叫线程C。线程B是阻塞的,但它暂时释放了obj的监视器,当前线程C是持有obj的监视器的,那儿在线程C中调用了obj.notify就是在通知线程B回收暂时释放的obj的监视器,同时会将线程B转为就绪状态。那么这个时候线程B是否就回收了obj的监视器呢?答案是:没有。那什么时候回收呢?当线程B从就绪状态转为执行状态时,线程B就是立即回收obj的监视器,然后执行obj的方法2。

下面是我实际测试时用的代码,通过分析运行结果和我上面的结论相信很好理解。

package individual.hcx.test;

public class Test {
public static void main(String[] args) throws Exception{
StringBuffer sb = new StringBuffer();
MyThread thread = new MyThread(sb);
MyThread2 thread2 = new MyThread2(sb);

thread2.start();
Thread.sleep(100);
thread.start();
}
}

class MyThread extends Thread{
StringBuffer sb;
public MyThread(StringBuffer sb){
this.sb = sb;
}

@Override
public void run(){
do{
synchronized(sb)
{
System.out.println("生成随机数!");
sb.delete(0, sb.length());
sb.append((int)(Math.random()*100));

sb.notify();
}
}while(true);
}
}

class MyThread2 extends Thread{
StringBuffer sb;
public MyThread2(StringBuffer sb){
this.sb = sb;
}
@Override
public void run(){
synchronized (sb) {
while(true){
System.out.println("sb = " + sb);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
try {
sb.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

在最后,wait()方法有两个重载的方法,他们的作用是让线程阻塞特定时间后还没有获得notify的通知,自动自己回收对象监视器。notify和notifyAll方法的却别是,notify是从全部等待的线程中随机选取一个通知它,notifyAll是通知全部在等待的线程。

0 0