线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步执行,但是如果每个线程都孤立的运行,就会造成资源的浪费.如果在现实中需要多个线程按照指定的规则共同完成一件任务,那么线程间就需要互相协调,这个过程被称为线程的通信.
当多个线程共同操作共享资源时,线程间通过某种方式互相告知自己的状态,以免无效的资源争夺.
1等待-通知 2共享内存 3管道流
public synchronized void add(T element) throws Exception
{
if (amount.get() > MAX_AMOUNT)
{
Print.tcfo("队列已经满了!");
return;
}
...
}
看这个伪代码,不管缓冲区是否满了都需要进行一次判断.
public synchronized T fetch() throws Exception
{
if (amount.get() <= 0)
{
Print.tcfo("队列已经空了!");
return null;
}
...
}
当缓冲区为空时,无法取出数据时还是要在判断一次.
针对上述情况可以通过等待-通知方式进行生产者-消费者之间的线程通信.
数据缓冲区满的时候,可以让生产者等待,等到缓冲区可以放入数据的时候唤醒生产者线程.
数据缓冲区空的时候,可以让消费等待,等到缓冲区可以消费数据的时候唤醒消费者线程.
可以由消费者取出数据的时候去唤醒等待的生产者线程.
可以由生产者放入数据的时候去唤醒等待的消费者线程.
java语言中等待-通知方式的线程间通信使用对象的wait() notify()两类方法实现.
1对象的wait()方法
对象的wait()方法主要作用就是让当前线程阻塞并且被等待唤醒.wait()方法与对象的监视锁密切相关,使用wait()方法也要放到同步块中.
synchronized (lock){
//同步保护的代码块
lock.wait();
...
}
Object类中wait方法有三个版本
public final native void wait(long timeout) throws InterruptedException;
这是一个基础版本,当线程调用了同步对象的wait方法后,当前线程会进入等待被唤醒状态.
public final native void wait(long timeout) throws InterruptedException;
这是一个限时等待状态,当线程调用了同步对象的这个方法后,当前线程会等待其他线程唤醒或者指定时间结束,不在被阻塞.
public final void wait(long timeout, int nanos) throws InterruptedException
这是一个高精度的等待版本,主要作用是更精确的控制等待时间.
2:wait方法的核心原理
当线程调用了某个锁对象的wait方法后,JVM会将当前线程加入锁对象监视器WaitSet(等待集)被其他线程唤醒.
当前线程会释放监视器的拥有权,让其他线程可以抢锁对象的监视器.
让当前线程等待,状态变成WAITING.
3:对象的notify方法
对象的notify方法主要是唤醒等待中的线程,notify方法与对象的监视锁密切相关,使用notify()方法也要放到同步块中.
synchronized (lock){
//同步保护的代码块
lock.notify();
...
}
notify方法有两个版本
public final native void notify();
锁对象的notify方法被调用后,唤醒锁对象监视器等待集合中的第一个等待线程.被唤醒的线程进入EntryList.状态从WAITING等待状态进入BLOCKED.
public final native void notifyAll();
锁对象的notifyAll方法被调用后,唤醒锁对象监视器等待集合中所有的等待线程.所有被唤醒的线程进入EntryList.状态从WAITING等待状态进入BLOCKED.
4:notify方法的核心原理
当前线程调用了锁对象的notify方法后,JVM会唤醒锁对象监视器WaitSet中的第一个线程.
当前线程调用了锁对象的notifyAll方法后,JVM会唤醒锁对象监视器WaitSet中的所有线程.
等待线程被唤醒后,会从监视器的WaitSet移动到EntryList中,线程具备了排队抢夺监视器的资格.状态从WAITING等待状态进入BLOCKED.
EntryList中的线程抢夺到监视器权利后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格.
public class WaitNotifyTest {
static Object lock = new Object();
//等待线程的异步任务.
static class WaitTarget implements Runnable {
@Override
public void run() {
//加锁.
synchronized (lock) {
System.out.println("启动等待");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("收到通知继续执行");
}
}
}
//等待线程的异步任务.
static class NotifyTarget implements Runnable {
@Override
public void run() {
//加锁.
synchronized (lock) {
System.out.println("开始唤醒");
lock.notifyAll();
System.out.println("发出通知了,但是线程还没有立马释放锁");
}
}
public static void main(String[] args) throws InterruptedException {
Thread waitThread = new Thread(new WaitTarget(), "等待线程");
waitThread.start();
Thread.sleep(1000);
Thread notifyThread = new Thread(new NotifyTarget(), "等待线程");
notifyThread.start();
}
}
}