wait()、sleep()、notify()、notifyAll()、interrupt()、join()、yield() 、interrupted()所属类及其作用分析

来源:互联网 发布:上海政府数据开放平台 编辑:程序博客网 时间:2024/06/07 01:36

参考资源:

http://blog.csdn.net/zyplus/article/details/6672775


notify()、notifyAll()、wait()属于java.lang.Object,java.lang.Thread也是Object,自然也有上述方法;

sleep()、interrupt()、interrupted()、join()、yield()属于java.lang.Thread

wait()方法表示,放弃当前对资源的占有权,等啊等啊,一直等到有人通知我,我才会运行后面的代码。
notify()方法表示,当前的线程已经放弃对资源的占有,通知等待的线程来获得对资源的占有权,但是只有一个线程能够从wait状态中恢复,然后继续运行wait()后面的语句;
notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始运行。

        Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

 

        单单在概念上理解清楚了还不够,需要在实际的例子中进行测试才能更好的理解。对Object.wait(),Object.notify()的应用最经典的例子,应该是三线程打印ABC的问题了吧,这是一道比较经典的面试题,题目要求如下:

建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。代码如下:


<span style="font-size:14px;">package thread;public class MyThreadPrinterABC implements Runnable {private String name;private Object prev;private Object self;private MyThreadPrinterABC(String name, Object prev, Object self) {this.name = name;this.prev = prev;this.self = self;}@Overridepublic void run() {int count = 10;while (count > 0) {synchronized (prev) {synchronized (self) {System.out.print(name);count--;self.notify();}try {if (count > 0)prev.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}public static void main(String[] args) throws Exception {Object a = new Object();Object b = new Object();Object c = new Object();MyThreadPrinterABC pa = new MyThreadPrinterABC("A", c, a);MyThreadPrinterABC pb = new MyThreadPrinterABC("B", a, b);MyThreadPrinterABC pc = new MyThreadPrinterABC("C", b, c);new Thread(pa).start();Thread.sleep(1);new Thread(pb).start();Thread.sleep(1);new Thread(pc).start();Thread.sleep(1);}}</span>


下面继续说sleep()join()yield()

sleep()使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。

例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。

总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。

join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。

yield()方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。

A.join,在API中的解释是,堵塞当前线程B,直到A执行完毕并死掉,再执行B;A.yield,A让出位置,给B执行,B执行结束A再执行。跟join意思正好相反!

sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。

yield()也不会释放锁标志。

实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。


sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。


yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。


最后说一下interrupt和interrupted,直接看例子:

package thread;public class TestInterrupt implements Runnable {int i = 10;@Overridepublic void run() {while (i-- > 0) {System.out.println("I am running!");try {Thread.sleep(100);} catch (InterruptedException e) {System.out.println("--------- InterruptedException ----------");continue;}}}public static void main(String[] args) throws InterruptedException {Thread t = new Thread(new TestInterrupt());t.start();Thread.sleep(500);// 运行一断时间后中断线程System.out.println("****************************");System.out.println("Interrupted Thread!");System.out.println("****************************");t.interrupt();//你以为到这里会中断吗?}}

你以为线程运行到t.interrupt();就会中断吗?看看结果:

I am running!I am running!I am running!I am running!I am running!I am running!****************************Interrupted Thread!****************************--------- InterruptedException ----------I am running!I am running!I am running!I am running!

虽然中断发生了,但线程仍然在进行,离开线程有两种常用的方法:
抛出InterruptedException和用Thread.interrupted()检查是否发生中断,下面分别看一下这两种方法:
1.在阻塞操作时如Thread.sleep()时被中断会抛出InterruptedException(注意,进行不能中断的IO操作而阻塞和要获得对象的锁调用对象的synchronized方法而阻塞时不会抛出InterruptedException),代码如下:

public void run() {          //死循环执行打印"I am running!"         try {              while (true) {                  System.out.println("I am running!");                                    //休眠一断时间,中断时会抛出InterruptedException                  Thread.sleep(50);              }          } catch (InterruptedException e) {              System.out.println("TestInterrupt.run() interrupted!");          }      }  


2.Thread.interrupted()检查是否发生中断.Thread.interrupted()能告诉你线程是否发生中断,并将清除中断状态标记,所以程序不会两次通知你线程发生了中断.代码如下:

 public void run() {                    //检查程序是否发生中断          while (!Thread.interrupted()) {              System.out.println("I am running!");            }            System.out.println("TestInterrupt.run() interrupted!");      }  


0 0
原创粉丝点击