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

本文详细解析了Java多线程中的wait、notify及notifyAll方法,并通过实例演示了这些方法的具体应用,包括如何控制线程间的等待与唤醒机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章主要讲解多线程编程中 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();
		//线程T1
		Thread 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...");
			}
		});
		
		//线程T2
		Thread 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);
			}
			@Override
			public 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则相反,它先唤醒最后一个被阻塞的线程!
现在我们知道了这几个方法的基本用法,下一章我们来写一 个“积累能量---放大招”  的例子来整合演示这几个方法的协作运用;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值