学科分类
目录
Java基础

问题如何解决

如果想解决上述问题,就需要控制多个线程按照一定的顺序轮流执行,此时就需要让线程间进行通信,保证线程任务的协调进行。为此,Java在Object类中提供了wait()、notify()、notifyAll()等方法用于解决线程间的通信问题,因为Java中所有类都是Object类的子类或间接子类,因此任何类的实例对象都可以直接使用这些方法。接下来针对这几个方法进行简要说明,如表1所示。

表1 线程通信的常用方法

方法声明 功能描述
void wait() 使当前线程放弃同步锁并进入等待,直到其他线程进入此同步锁,并调用notify()或notifyAll()方法唤醒该线程为止
void notify() 唤醒此同步锁上等待的第一个调用wait()方法的线程
void notifyAll() 唤醒此同步锁上调用wait()方法的所有线程

表1中,列出了3个与线程通信相关的方法,其中wait()方法用于使当前线程进入等待状态,notify()和notifyAll()方法用于唤醒当前处于等待状态的线程。需要注意的是,wait()、notify()和notifyAll()这三个方法的调用者都应该是同步锁对象,如果这三个方法的调用者不是同步锁对象,Java虚拟机就会抛出IllegalMonitorStateException异常。

接下来通过使用wait()和notify()方法,实现线程间的通信,如文件1所示。

文件1 Example17.java

 1    import java.util.*;
 2    public class Example17 {
 3        public static void main(String[] args) {
 4            // 定义一个集合类,模拟存储生产的商品
 5            List<Object> goods = new ArrayList<>();
 6            // 记录线程执行前统一的起始时间start
 7            long start = System.currentTimeMillis();
 8            // 创建一个生产者线程,用于生产商品并存入商品集合
 9            Thread thread1 = new Thread(() -> {
 10                int num = 0;
 11                while (System.currentTimeMillis()-start<=100) {
 12                    // 使用synchronized关键字同步商品生产和消费
 13                    synchronized (goods) {
 14                        // 有商品就让生产者进入等待状态
 15                        if(goods.size() >0){
 16                            try {
 17                                goods.wait();
 18                            } catch (InterruptedException e) {
 19                                e.printStackTrace();
 20                            }
 21                        }else{
 22                            // 生产者继续生产商品
 23                            goods.add("商品" + ++num);
 24                            System.out.println("生产商品" + num);
 25                        }
 26                    }
 27                }
 28            }, "生产者");
 29            // 创建一个消费线程,用于消费商品并将商品从集合删除
 30            Thread thread2 = new Thread(() -> {
 31                int num = 0;
 32                while (System.currentTimeMillis()-start<=100) {
 33                    // 使用synchronized关键字同步商品消费和消费
 34                    synchronized (goods) {
 35                        // 商品不足就唤醒生产者进行生产
 36                        if(goods.size()<= 0){
 37                            goods.notify();
 38                        }else{
 39                            // 继续消费商品
 40                            goods.remove("商品" + ++num);
 41                            System.out.println("消费商品" + num);
 42                        }
 43                    }
 44                }
 45            }, "消费者");
 46            // 同时启动生产者和消费者两个线程,并统一执行100毫秒的时间
 47            thread1.start();
 48            thread2.start();
 49        }
 50    }

再次运行结果如图1所示。

图1 运行结果

文件1中,在生产者和消费者线程的两个执行任务中同时使用synchronized关键字同步商品生产和消费,之后每生产出商品,便调用wait()方法将当前线程置于等待状态,等待消费者线程进行消费,当消费者线程执行任务发现没有商品时便调用notify()方法唤醒对应同步锁上等待的生成者线程,让生产者线程继续生产,从而持续达到供需有序、均衡。从图1可以看出,生产者线程和消费者线程按照先生产后消费的顺序轮流执行,不再出现供需节奏不一致的问题。

需要说明的是,Java为线程等待方法wait()提供了多个重载方法,包括无参wait()方法、有等待时间的wait(long timeout)方法和wait(long timeout, int nanos)方法。其中,带有等待时间参数的wait()方法,除了会在其他线程对象调用notify()和notifyAll()方法来唤醒当前处于等待状态的线程,还会在等待时间过后自动唤醒处于等待状态的线程。

点击此处
隐藏目录