线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定
的代码逐步执行,但是如果每个线程间都孤立地运行,就会造资源浪
费。
所以在现实中,如果需要多个线程按照指定的规则共同完成一个任
务,那么这些线程之间就需要互相协调,这个过程被称为线程的通信。
线程的通信可以被定义为:当多个线程共同操作共享的资源时,线
程间通过某种方式互相告知自己的状态,以避免无效的资源争夺。
线程间通信的方式可以有很多种:等待-通知、共享内存、管道
流。每种方式用不同的方法来实现,这里首先介绍等待-通知的通信方
式。
“等待-通知”通信方式是Java中使用普遍的线程间通信方式,其经典
的案例是“生产者-消费者”模式
Java语言中“等待-通知”方式的线程间通信使用对象的wait()、
notify()两类方法来实现。每个Java对象都有wait()、notify()两类实例方
法,并且wait()、notify()方法和对象的监视器是紧密相关的。
Java中的wait和notify方法是线程间通信和同步的核心机制,它们位于java.lang.Object类中,允许线程在特定条件下挂起和恢复执行。以下是这两个方法的原理及使用例子:
wait方法
- 当一个线程调用对象的
wait()方法时,它会释放该对象的监视器锁(也称为对象锁或 intrinsic lock),并进入等待(阻塞)状态。 - 调用
wait()方法必须在同步代码块(即synchronized修饰的代码块或方法)内进行,否则会抛出IllegalMonitorStateException异常。 - 线程调用
wait()时,会一直等待,直到其他线程调用同一个对象上的notify()或notifyAll()方法,或者超过指定的等待时间(如果有传递超时参数给wait(long timeout))。
例如:
class SharedResource {
private boolean dataAvailable = false;
public synchronized void produce() {
while (dataAvailable) { // 数据已满,生产者线程等待
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 处理中断
}
}
// 生产数据...
dataAvailable = true;
// 唤醒等待的消费者线程
notify();
}
public synchronized void consume() {
while (!dataAvailable) { // 数据为空,消费者线程等待
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 消费数据...
dataAvailable = false;
// 唤醒等待的生产者线程
notify();
}
}
在这个例子中,有两个线程分别扮演生产者和消费者的角色,共享一个资源。生产者在资源已满时调用wait(),释放锁并等待;消费者在资源空时同样调用wait()。当资源状态改变时,相应线程通过调用notify()来唤醒对方。
notify方法
notify()方法用于唤醒正在等待(因为调用了wait())同一对象监视器锁的一个线程。- 它并不能确切指定唤醒哪个线程,而是从等待集合中随机选择一个线程,将其移出等待队列并放入可运行队列,然后该线程有机会获取锁并继续执行。
如果需要唤醒所有等待的线程,则应使用notifyAll()方法,它会唤醒所有等待在该对象上的线程。
注意:
- 使用
wait()和notify()时必须正确处理异常,并且通常建议在循环中检查条件,因为即使线程被唤醒,条件可能仍未满足。 - 这些方法基于“先入先出”(FIFO)原则,但不保证绝对的公平性,且在JDK的不同版本中其行为可能会有所变化。
- 在现代Java编程中,推荐使用更高级别的同步框架如
java.util.concurrent包下的工具类(如Semaphore、CountDownLatch、BlockingQueue等)来替代直接使用wait()和notify(),这些工具类通常提供了更好的可读性和更强的线程同步控制能力。
8282

被折叠的 条评论
为什么被折叠?



