java线程等待/通知机制及中断

来源:互联网 发布:厦门第二世界网络 编辑:程序博客网 时间:2024/05/19 22:51

一、等待/通知机制

 在线程交互中经常需要对其进行一些控制,希望人为地能够让线程按理想路线发展,在满足某条件时进行执行操作而发生变化时,停止等待。

1、 使用sleep 

在 if ( ) { } else { }  中使用sleep 对线程进行停止等待一段时间。   弊端:正常情况下 无法客观预知需要等待的时间,在刻意睡眠一段时间后 很可能发现 依旧不适合由此线程执行之后的操作,或者睡眠过久。

2、 使用 while + sleep   循环判断条件 使其睡眠    弊端:虽然能加快判断条件的变化,但依旧难以确保及时性,会造成无端浪费。

3、wait +notify :在某条件发生情况下,线程A调用对象O 的wait() 方法进入等待状态,当线程B调用对象O的notify() 或者notifyAll()方法后,线程A会接受通知,从其wait方法返回,执行后续操作。

java.lang.Obejct  :

notify()  通知一个在对象上等待的线程,使其从wait方法返回(前提是该线程获取到对象的锁)

notifyAll() 通知所有等待在该对象上的线程 (注意,notify等通知时 不会释放当前对象锁)

wait()  调用该方法的线程进入Waiting状态,只有被中断或者由其他线程通知唤醒才能继续(wait会导致线程释放对象锁)

wait(long) 超时等待一段时间,等待xx毫秒,若没有收到通知 则超时返回

wait(long,int)超时精确到纳秒

例:

import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.TimeUnit;public class WaitAndNotify {static boolean flag=true;static Object lock =new Object();public static void main(String[] args) throws InterruptedException {Thread waitThread=new Thread(new Wait(),"waitThread");waitThread.start();TimeUnit.SECONDS.sleep(2);Thread notifyThread=new Thread(new Notify(),"notifyThread");notifyThread.start();}static class Wait implements Runnable{@Overridepublic void run() {synchronized (lock) {//同步代码块while (flag) {try {System.out.println(Thread.currentThread().getName()+" flag=true. wait@"+new SimpleDateFormat("HH:mm:ss").format(new Date()));lock.wait();<span style="white-space:pre"></span>System.out.println("啊啊?");} catch (InterruptedException e) {e.printStackTrace();}}//跳出while时System.out.println(Thread.currentThread().getName()+" flag=false. wait@"+new SimpleDateFormat("HH:mm:ss").format(new Date()));}} }static class Notify implements Runnable{@Overridepublic void run() {synchronized (lock) {//获取lock对象锁,然后通知唤醒System.out.println(Thread.currentThread().getName()+" hold lock. notify@"+new SimpleDateFormat("HH:mm:ss").format(new Date()));lock.notifyAll();flag=false;try {Thread.sleep(3000);//notify之后线程睡眠3秒,验证Wait类不能马上输出“啊啊?”} catch (InterruptedException e) {e.printStackTrace();}} //同步代码块结束后 释放锁synchronized (lock) {//再次加锁System.out.println(Thread.currentThread().getName()+" hold lock again. @"+new SimpleDateFormat("HH:mm:ss").format(new Date()));try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}}}
运行结果:(注:在notifyThread里的两个同步代码块之间 可能会发生lock被Wait获取 而使输出结果的第三、四行互换)

waitThread flag=true. wait@16:00:44notifyThread hold lock. notify@16:00:46notifyThread hold lock again. @16:00:49啊啊?waitThread flag=false. wait@16:00:51

waitThread 获取到lock对象锁,之后调用wait()方法进入等待队列,而同时会释放掉对象锁,状态为Waiting。  notifyThread获取lock对象锁后调用notify()方法通知一个等待线程(本例中仅一个等待线程),将其移到同步队列,然后继续执行自己的代码,当释放掉lock对象锁后,waitThread线程才有可能重新获取lock并执行先前未完成的代码。   借助《Java并发编程的艺术》中一图:


注意:

1)wait() notify() notifyAll() 等使用时 需要先对调用的对象加锁获取。

2)wait()之后 线程由Running转变为Waiting 将会把当前线程防止到对象的等待队列。

3)并不是一旦使用notify() notifyAll() 就能实现线程执行wait()方法之后的代码段,需要等发出notify()的线程先释放对象锁然后 等待线程重新获得lock锁 才可以执行原wait()之后的操作!

4)notify()方法将等待队列中的一个等待线程从其中移到同步队列中,而notifyAll()方法则是唤醒等待队列中的所有线程,全部移到同步队列,将Waiting改为Blocked

等待方:①.获取对象锁 ②.若条件不满足则调用对象wait(),被通知后要重新判断条件(可能会伪唤醒)③.满足条件后执行后续操作

通知方:①.获取对象锁 ②.改变条件 ③.通知等待的线程


二、中断及join()

interrupt()只是改变中断状态而已. interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,给受阻塞的线程抛出一个中断信号,这样受阻线程就得以退出阻塞的状态。如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。

join() 含义:若线程A执行某thread线程的join(),那么当前线程A等待thread线程终止之后才能从join()返回

例: 每个线程调用前一个线程的join()  按顺序结束操作。

import java.util.concurrent.TimeUnit;public class Test2 {public static void main(String[] args) throws InterruptedException {Thread[] myThreads =new Thread[5];Thread previous=Thread.currentThread();for(int i=0;i<5;i++){myThreads[i]=new Thread(new Runner(previous),i+1+" Thread");myThreads[i].start();previous=myThreads[i];}//TimeUnit.SECONDS.sleep(3);//main sleep 3s//myThreads[3].interrupt();TimeUnit.SECONDS.sleep(3);//main sleep 3sSystem.out.println(Thread.currentThread().getName()+" terminate");} static class Runner implements Runnable{ private Thread aThread; public Runner( Thread aThread) {this.aThread=aThread;}@Overridepublic void run() {try {aThread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" terminate");} }}
运行结果:

main terminate1 Thread terminate2 Thread terminate3 Thread terminate4 Thread terminate5 Thread terminate


若打开 // myThreads[3].interrupt(); 注释,则会报java.lang.InterruptedException 然后先结束4、5 

再结束mian 1 2 3   注:由于线程一直运行,在报错的同时,已经在输出4的语句

java.lang.InterruptedException4 Thread terminateat java.lang.Object.wait(Native Method)at java.lang.Thread.join(Thread.java:1249)at java.lang.Thread.join(Thread.java:1323)at Test2$Runner.run(Test2.java:25)at java.lang.Thread.run(Thread.java:745)5 Thread terminatemain terminate1 Thread terminate2 Thread terminate3 Thread terminate


 




0 0
原创粉丝点击