java - 子线程的join方法阻塞主线程,如果在另一个子线程中notify那个子线程,会报监视器状态异常,为什么?
PHPz
PHPz 2017-04-17 17:58:34
0
1
832

一.问题
当主线程调用子线程的join方法时,其实还是调用子线程的wait方法来阻塞主线程,那么有两个问题:
a.如果我在另一个子线程中获得当前子线程对象,并调用线程的notify方法,是不是可以解除子线程的阻塞,经测试会报监视器状态异常。
b.子线程是个单独的对象,为啥会阻塞主线程呢?又不存在共享资源竞争,尤其是Thread中join方法是个普通的synchronized方法

二、代码
public class JoinTest {

public static void main(String[] args) throws InterruptedException {
    MyThread3 thread=new MyThread3();
    NotifyThread nt=new NotifyThread(thread);
    thread.start();
    nt.start();
    thread.join();
    for(int i=0;i<3;i++){
        System.out.println(Thread.currentThread().getName() + "线程第" + i + "次执行!");
    }
}

}
class NotifyThread extends Thread{

Thread myThread ;
public NotifyThread(Thread myThread){
    this.myThread=myThread;
}
public void run(){
    try {
        System.out.println("休眠开始");
        Thread.sleep(3000);
        System.out.println("休眠结束");
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    myThread.notify();
    System.out.println("已唤醒,让Join失效");
}

}

class MyThread3 extends Thread {

@Override
public void run() {
    
    for (int i = 0; i < 10; i++) {
        try {
            System.out.println(this.getName() + "线程第" + i + "次执行!");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

三、异常
Thread-0线程第0次执行!
休眠开始
Thread-0线程第1次执行!
Thread-0线程第2次执行!
休眠结束
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException

at java.lang.Object.notify(Native Method)
at thread.com.simple.NotifyThread.run(JoinTest.java:29)

Thread-0线程第3次执行!
Thread-0线程第4次执行!
Thread-0线程第5次执行!
Thread-0线程第6次执行!
Thread-0线程第7次执行!
Thread-0线程第8次执行!
Thread-0线程第9次执行!
main线程第0次执行!
main线程第1次执行!
main线程第2次执行!

PHPz
PHPz

学习是最好的投资!

Antworte allen(1)
伊谢尔伦

先谈为什么“唤醒”不了,notify不会“唤醒”MyThread3,因为阻塞不就是join方法的使命么?再说MyThread3也没有休眠,不是一直在执行么,何来“唤醒”之说!

最后来看看join方法的实现,或许对你理解有帮助:

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

再来谈为什么有异常,其实已经比较明确了,看注释:

/**
 * Thrown to indicate that a thread has attempted to wait on an
 * object's monitor or to notify other threads waiting on an object's
 * monitor without owning the specified monitor.
 */

当前线程不是该对象monitor所有者时,试图调用其wait或者notify方法就会报这个错,解决异常只要拿到所有者就好了,常见方法:

public class NotifyThread extends Thread{

    Thread myThread ;
    public NotifyThread(Thread myThread){
        this.myThread=myThread;
    }

    public void run(){
        try {
            System.out.println("休眠开始");
            Thread.sleep(3000);
            System.out.println("休眠结束");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        synchronized(myThread){
            myThread.notify();
        }
        System.out.println("已唤醒,让Join失效");
    }
}
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage