基础篇:wait与notify与notifyAll(八)

来源:互联网 发布:网络客户服务的优势有 编辑:程序博客网 时间:2024/06/07 04:04

这篇文章主要讲解多线程编程中 wait与notify与notifyAll 这三个方法的运用,了解了它们的基本用法后,我们再写个 “积累能量---放大招”  的例子来整合演示这几个方法的协作运用;


见名之意,wait是等待的意思,当在线程A内调用wait()时,线程A将暂停运行,直到其它的线程通过调用notify或者notifyAll方法唤醒它为止;

这三个方法都是从Object类继承而来的,它们必须运行在持锁的代码中,否则将抛出异常,想一想,为什么这些方法会定义在Object中,又为什么必须运行在持锁的情况下呢?答案提示:想想同步的意义...



wait与notifyAll的用法:

我们先下一个结论:wait是会释放锁的,而sleep是不会释放锁的!接下来我们来看代码:

class TempObj{//用来证明sleep是不会释放锁的public synchronized void bySleep(){System.out.println("tempObj.bySleep()");try {TimeUnit.MILLISECONDS.sleep(20000);} catch (InterruptedException e) {e.printStackTrace();}}//用来证明wait是会释放锁的public synchronized void byWait(){System.out.println("tempObj.byWait()");//调用wait,使当前线程进入等待状态try {wait();} catch (InterruptedException e) {e.printStackTrace();}}public synchronized void show(){//正常输出内容System.out.println("tempObj.show()");} }

//演示sleep不会释放锁,而wait是会释放锁的public static void sleepAndWait() throws InterruptedException{final TempObj obj = new TempObj();//线程T1Thread t1 = new Thread(new Runnable() {public void run() {System.out.println("run thread t1");//obj.bySleep(); //代码1处obj.byWait();System.out.println("run thread t1...over...");}});//线程T2Thread t2 = new Thread(new Runnable() {public void run() {System.out.println("run thread t2");obj.show();System.out.println("run thread t2...over");}});ExecutorService exec = Executors.newCachedThreadPool();exec.execute(t1);TimeUnit.MILLISECONDS.sleep(10);exec.execute(t2);exec.shutdown();}
输出******************************************************************

run thread t1
tempObj.byWait()
run thread t2
tempObj.show()
run thread t2...over

**********************************************************************'

我们开启了两个线程T1以及T2,t1先运行,首先打印出【run thread t1】,

它在内部将调用TempObj的byWait方法,接着打印出【tempObj.byWait()】,

接下来在它内部调用了wait(),这将导致当前线程阻塞且释放锁,所以没有打印出【run thread t1...over...】;

而T2线程调用了TempObj的show方法,此时T1已释放了锁,所以能打印出【tempObj.show()】

如果我们将obj.byWait()方法注释,然后将Obj.bySleep()注释取消,则结果又会是怎样呢,我们来看看输出:


输出******************************************************************

run thread t1
tempObj.bySleep()
run thread t2

**********************************************************************'


由于sleep是不会释放锁的,所以T2线程自然无法调用show()方法咯!

我们注意到第一次输出还少了个内容【run thread t1...over...】 ,这是因为T1线程一直在等待,而T2线程直接运行完毕并没有唤醒它,我们稍微改一下代码:
public synchronized void show(){//正常输出内容System.out.println("tempObj.show()");//唤醒正在等待当前对象锁的所有线程,T1线程正在等待此对象锁notifyAll();} 

再次运行,输出**************************************************************
run thread t1
tempObj.byWait()
run thread t2
tempObj.show()
run thread t2...over
run thread t1...over...
******************************************************************************
没错,T2线程与T1线程都运行完毕,这就是我们想要的!

现在我们来思考一个问题,notify只会唤醒等待当前对象锁的单个线程,但它到底会唤醒哪个线程呢?是随机的吗?  notifyAll将唤醒等待当前对象锁的全部线程,那唤醒的顺序又是怎样的呢?我们写段代码来试验一下!

//演示notify,notifyAll 的唤醒顺序public static void notifyAndNotifyAll() throws InterruptedException{class Temp{public synchronized void show(){System.out.println(Thread.currentThread().getName()+".temp.show()");try {wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+".temp.show()...over...");}}final Temp temp = new Temp();class MyThread extends Thread{public MyThread( String name ) {super.setName(name);}@Overridepublic void run() {temp.show();}}ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {exec.execute(new MyThread("线程"+i));}TimeUnit.MILLISECONDS.sleep(1000);//注意,wait以及notify等方法调用,必须是在持有其对象锁的情况下!synchronized (temp) {temp.notify();//temp.notifyAll();}exec.shutdown();}

这代码比较简单,我们开启5个线程去调用temp.show(),这里将先输出【线程名字.temp.show()】,然后被阻塞, 被阻塞后,我们分别调用notify()和notifyAll()看看线程被唤醒的顺序 ; 我们来看notify()
输出**************************************************************************
pool-1-thread-1.temp.show()
pool-1-thread-4.temp.show()
pool-1-thread-3.temp.show()
pool-1-thread-5.temp.show()
pool-1-thread-2.temp.show()
pool-1-thread-1.temp.show()...over...
*******************************************************************************

我们在将注释取消,看看notifyAll()
输出**************************************************************************
pool-1-thread-2.temp.show()
pool-1-thread-3.temp.show()
pool-1-thread-1.temp.show()
pool-1-thread-5.temp.show()
pool-1-thread-4.temp.show()
pool-1-thread-4.temp.show()...over...
pool-1-thread-5.temp.show()...over...
pool-1-thread-1.temp.show()...over...
pool-1-thread-3.temp.show()...over...
pool-1-thread-2.temp.show()...over...

******************************************************************************


Ok,通过输出我们得出结论;notify是先唤醒第一个被阻塞的线程; 而notifyAll则相反,它先唤醒最后一个被阻塞的线程!
现在我们知道了这几个方法的基本用法,下一章我们来写一 个“积累能量---放大招”  的例子来整合演示这几个方法的协作运用;

0 0
原创粉丝点击