电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

线程等待和释放的小麻烦(wait/notify)


发布日期:2024/4/24
 

对于初学者来说下面这个例子是一个非常常见的错误

/**

* 线程A 循环次后等待并放弃锁让线程B执行

*/

class ThreadA extends Thread{

//线程同步的公共数据区

Object oa=null;

ThreadA(Object o){

thisoa=o;

}

//线程A执行逻辑

public void run(){

//线程同步区域需要申请公共数据的锁

synchronized(oa){

Systemoutprintln(ThreadA is running);

for(int i=;i<;i++){

Systemoutprintln( ThreadA value is +i);

if(i==){

try {

//当前线程等待

ThreadcurrentThread()wait();

} catch (InterruptedException e) {

eprintStackTrace();

}

}//if(i==)

}//for(int i)

}

}

}

/**

* 线程B等待线程A放弃锁然后获得锁并执行完成后唤醒线程A

*/

class ThreadB extends Thread{

//线程同步的公共数据区

Object ob=null;

ThreadB(Object o){

thisob=o;

}

//线程B执行逻辑

public void run(){

//线程同步区域需要申请公共数据的锁

synchronized(ob){

Systemoutprintln(ThreadB is running);

for(int i=;i<;i++){

Systemoutprintln( ThreadB value is +i);

}

//唤醒等待的线程

notify();

}

}

}

//测试

public class ThreadTest {

public static void main(String[] args){

Object lock=new Object(); //公共数据区

ThreadA threada=new ThreadA(lock);

ThreadB threadb=new ThreadB(lock);

threadastart(); //线程A执行

threadbstart(); //线程B执行

}

} 程序很简单就是让线程AB交替打印但是运行的时候会抛出两个异常

Exception in thread Thread javalangIllegalMonitorStateException: current thread not owner

Exception in thread Thread javalangIllegalMonitorStateException: current thread not owner

问题就处在ThreadA中的ThreadcurrentThread()wait(); 和ThreadB中的notify();上

初学者理解wait()的时候都认为是将当前线程阻塞所以ThreadcurrentThread()wairt();视乎很有道理但是不知道大家有没有发现在JDK类库中wait()和notify()方法并不是Thread类的而是Object()中的我们仔细看看wait方法的JDK文档

public final void wait() throws InterruptedException

Object (Java Platform SE )<! Generated by javadoc (build beta) on Thu Jan :: CST > <script type=text/javascript> function windowTitle() { if (locationhrefindexOf(isexternal=true) == ) { parentdocumenttitle=&quot;Object (Java Platform SE )&quot;; } } </script>

<noscript></noscript>

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前导致当前线程等待 换句话说此方法的行为就好像它仅执行 wait() 调用一样

当前线程必须拥有此 对象监视器 该线程发布对此监视器的所有权并等待 直到其他线程通过调用 notify 方法或 notifyAll 方法通知在此对象的监视器上等待的线程醒来 然后该线程将等到重新获得对监视器的所有权后才能继续执行

对于某一个参数的版本实现中断和虚假唤醒是可能的而且此方法应始终在循环中使用

synchronized (obj) {

while (<condition does not hold>)

objwait();

// Perform action appropriate to condition

}

此方法只应由作为此对象监视器的所有者的线程来调用

抛出 IllegalMonitorStateException 如果当前线程不是此对象监视器的所有者

InterruptedException 如果在当前线程等待通知之前或者正在等待通知时任何线程中断了当前线程在抛出此异常时当前线程的中断状态 被清除

看完JDK文档以后很显然只要把开始的程序中ThreadcurrentThread()wait();改成oawait() notify();改成 obnotify()就没有问题了

也就是说只能通过同步块obj来调用wait/notify方法 而不能通过想当然的线程调用这两个方法至于为什么是这样我有一种想法大家可以一起讨论一下

首先我们都知道JVM会给每一个对象都分配唯一的一把锁这把锁是在对象中的

然后当Thread线程获得了这把锁后应该是在对象中的锁内记录下当前占有自己的线程号并把自己设置为已被占用那么当Thread需要放弃锁的时候锁对象会吧 Thread放入到锁的等待队列中 而这一切和Thread是没有任何关系的自然也轮不到Thread对象来调用某个方法来改变另一个对象中的锁(这一点也说不通我自己的锁凭什么让你来改)

因此也就出现用改变公共数据区对象的锁的方法是通过共数据区对象本省来调用和线程对象是没有关系的

上一篇:Groovy编程—Annotations的使用

下一篇:Neptune任务构建系统的实例