详解wait/notify机制,看这一篇就够了

1 wait/notify 机制的原理

1.1 wait()方法是Object 类的方法,它的作用是使当前执行wait()方法的线程等待,在wait()所在的代码行处暂停执行,并释放锁,直到接到通知或中断。

1.2 notify()方法用来通知那些可能等待该锁的其他线程,如果有多个线程等待,则按照执行wait方法的顺序发出一次性通知(一次只能通知一个!),使得等待排在第一顺序的线程获得锁。需要说明的是,执行notify方法后,当前线程并不会立即释放锁,要等到程序执行完,即退出synchronized同步区域后

总结:wait 方法使线程暂停运行,而notify 方法通知暂停的线程继续运行。

要想正确使用wait/notify,一定要注意:
wait/notify在调用前一定要获得相同的锁,如果在调用前没有获得锁,程序会抛出异常,也就调用不了wait/notify;另外,如果获得的不是同一把锁,notify不起作用。

2 wait() 方法的基本使用

2.1 在使用前没有获得锁,抛出异常

public class Test {

	public static void main(String[] args) throws Exception {
		
		try {
			
			Object object = new Object();
			object.wait();
		}catch (Exception e) {
			e.printStackTrace();
		}
					
	}
}

在这里插入图片描述

2.2 在使用前先获得锁,正确调用wait,无异常抛出

public class Test {

	public static void main(String[] args) throws Exception {
		
		try {
			
			Object object = new Object();
			System.out.println("执行wait之前的代码");
			
			//获得锁
			synchronized(object) {
				object.wait();
				System.out.println("执行wait之后的代码");		
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
					
	}
}

在这里插入图片描述

3 完整实现 wait/notify

3.1 创建Service类

public class Service {

	Object lock = new Object();
	
	public void waitMethod() {
		
		synchronized(lock) {
			try {
				
				System.out.println(Thread.currentThread().getName()+"执行了wait方法,释放了锁");
				lock.wait();
				System.out.println(Thread.currentThread().getName()+"被唤醒了");
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public void notifyMethod() {
		
		synchronized(lock) {
			try {
				
				System.out.println(Thread.currentThread().getName()+"执行了notify方法");
				lock.notify();;
				System.out.println(Thread.currentThread().getName()+"继续执行notify后的代码,完事后才释放锁");
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
}

3.2 创建ThreadA线程类

public class ThreadA extends Thread {
	private Service service;
	
	public ThreadA(Service service) {
		
		this.service = service;
	}
	
	@Override
	public void run() {
		super.run();
		service.waitMethod();
	}

}

3.3 创建ThreadB线程类

public class ThreadB extends Thread {
	private Service service;
	
	public ThreadB(Service service) {
		
		this.service = service;
	}
	
	@Override
	public void run() {
		super.run();
		service.notifyMethod();
	}

}

3.4 测试与分析

public class Test {

	public static void main(String[] args) throws Exception {
		
		Service service = new Service();
		ThreadA threadA= new ThreadA(service);
		ThreadB threadB= new ThreadB(service);
		threadA.setName("A");
		threadB.setName("B");
		threadA.start();
		threadB.start();
		
					
	}
}

在这里插入图片描述

分析:
1、线程ThreadA启动后,获得lock锁,成功执行了wait方法,在wait方法所在行暂停,并且释放了锁
2、线程ThreadB获得ThreadA释放的锁,成功执行了notify方法并且通知到了ThreadA,告诉它做好苏醒准备,但ThreadB并没有马上释放锁,它继续执行,直到退出synchronzied 代码块
3、ThreadB退出synchronized 代码块后释放锁,ThreadA获得锁,从暂停的地方往后执行,直到程序执行完毕

Java中的锁机制是线程同步的关键,它用于控制多个线程对共享资源的访问,以避免数据竞争和并发问题。Java锁主要由`synchronized`关键字、`Lock`接口(如`ReentrantLock`)以及`Condition`类来实现。 **底层原理概述:** 1. **监视器(Monitor)和锁定:**每个对象都关联着一个监视器,当一个线程进入对象的`synchronized`代码块或方法时,它会获取该对象的监视器锁。如果另一个线程尝试获取同一对象的锁,那么线程会被阻塞直到第一个线程释放锁。 2. **互斥(Mutual Exclusion):**同一时间只有一个线程可以持有对象的锁,这就是互斥原则。其他等待的线程必须等到当前线程执行完毕并释放锁。 3. **可见性(Visibility):**当一个线程修改了共享状态后,必须调用`notify()`或`notifyAll()`方法通知其他等待的线程,使它们能够重新检查条件并获得锁。 4. **等待(Waiting)和唤醒(Signal):**`wait()`方法会让当前线程放弃锁,进入等待状态,直到被其他线程调用`notify()`或`notifyAll()`唤醒。如果等待过程中线程被中断,它会抛出`InterruptedException`。 5. **重入(Recursion):`synchronized`关键字支持一个线程在同一对象上多次获取锁,但只有在锁未被其他线程占用时才允许。 **ReentrantLock扩展:**Java的`ReentrantLock`提供了更多的灵活性,比如可选择公平锁(按等待线程顺序获取)和非公平锁(最快的线程优先获取),以及显式锁的获取和释放。此外,它还提供`tryLock()`方法用于无锁操作,以及条件变量`Condition`用于更复杂的同步场景。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值