天天看点

java多线程中wait()和notify()和notifyAll()及其他常用方法使用

一、首先说下线程的4种状态,任何一种线程都处于这4种状态之一。

1、产生(new):线程对象已经产生但是尚未被启动,所以无法执行,通过new产生对象后没有对它调用start()方法。

2、 可执行(Runnable):每一个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并且启动它,当一个线程处于可执行状态的时候时候, 表示它可能处于线程池中等待排程器启动它;也有可能它正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态。但是,显然线程可能 并不是在执行中。

3、 停滞(Blocks):当一个线程处于停滞状态的时候,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态的时候,它可能重 新执行。如通过对一个线程调用wait()方法后,线程就进入了停滞状态。只有对此调用notify()或者notifyAll()可使其回到可执行状 态。

4 、死亡(Dead):当一个线程正常结束,它处于停滞状态,如一个线程的run()方法执行完毕后。

二、线程中常用方法的使用介绍

1 、wait()之后notify()和notifyAll()的区别:

     wait 是Object 类的方法,对此对象调用wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 wait() 和notify()因为会对对象的“锁标志”进行操作,所以它们必须在 synchronized方法或synchronized block中进行调用。如果在non-synchronized方法或non- synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

这是一个wait()和notify()的例子:

线程A

synchronized(obj) {

while(!condition) {

obj.wait();

}

obj.doSomething();

}

当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。

在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:

线程B

synchronized(obj) {

condition = true;

obj.notify();

}

      notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法,当一个线程进入wait之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒所 有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。注意,任何时候只有一个线程可以获得锁,也就是说只有一个线程可以 运行synchronized中的代码,notifyall只是让处于wait的线程重新拥有锁的争夺权,但是只会有一个获得锁并执行。

补充说明下,notify() notifiAll()只会让线程从阻塞状态变成可执行状态,注意是 可运行,并不是说立即马上就会执行,对于处于可运行状态的多个线程来说,

由JVM根据当前线程池当中的可运行状态的线程的优先级来决定,即从runnable 到 run状态变化是由JVM来完成。

2、线程中其他常用方法的区别

(a)  sleep():使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;当前正在被服务的线程需要睡一会,醒来后继续被服务。由 于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休 眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过 interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

(b) yield():当前正在被服务的线程可能觉得cpu的服务质量不够好,于是提前退出,只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。 

sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。 

(c) join(): 方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被start之后不一定马上就运行,而是进入到可运行线程的队 列中,通过join可以运行。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。举例说明下: 当调用线程实例的start方法后,这个方法会立即返回,如果在调用start方法后后需要使用一个由这个线程计算得到的值,就必须使用join方法。如果不使用join方法,就不能保证当执行到start方法后面的某条语句时,这 个线程一定会执行完。而使用join方法后,直到这个线程退出,程序才会往下执行

(d)  interrupt() 

interrupt() 中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException。

继续阅读