接上章Java线程编程(基础知识点)。进一步讲解线程编程。
wait()方法和notify()方法。
wait()方法的本质是让执行这个方法的线程进入阻塞态;
notify()方法则相反,会唤醒处在阻塞态的相关线程。
在前面讲述进程状态变迁时,讲述过进程/线程从运行态变迁到阻塞态,会进入阻塞态多个阻塞队列中的一个队列里。为了能够唤醒这些进程/线程,系统需要知道究竟唤醒哪个队列中的进程/线程。
所以说,线程在阻塞态时,必须指明阻塞队列。
具体操作是,线程在阻塞时,必须指明一个“锁”,然后进入这个锁的阻塞队列。
所以,wait()方法必须在同步块中调用,并且必须提供锁对象!且,进程/线程的唤醒操作必须是由其它的进程/线程执行的!那么唤醒者也必须知道那个锁对象。
生产者——消费者问题分析
通过前面对生产者——消费者的问题描述可以知道,该问题涉及到两个方面,生产者(Producer)和消费者(Customer)。
下面我先给出一个接口;
接口ICommonObject:
package com.mec.about_thread.test;
import java.util.ArrayList;
import java.util.List;
//这里的操作很简单,我们只是定义了Object类型的lock对象,及一个存储数据的数组;
//因为是接口,真正实现是在Producer类和Customer类中。
public interface ICommonObject {
//锁对象
Object lock = new Object();
List<Integer> numPool = new ArrayList<>();
//set() 和 get() 就不需解释了。
default void setNum(int num) {
numPool.add(num);
}
//这里,因为我们需要知道这个numPool的情况,才能对Producer类和Customer类进行下一步操作。
//所以,我们只需的numPool这个数组进行判断。
default boolean isNumEmpty() {
return numPool.isEmpty();
}
default int getNum() {
return numPool.remove(0);
}
}
生产者Producer:
package com.mec.about_thread.test;
public class Producer implements Runnable {
private Thread thread;
private ICommonObject commonObject;
//这里我们可以通过内部类的形式实现找个借口,方法很多样
public Producer(String name) {
this.commonObject = new InnerProducer();
this.thread = new Thread(this, name);
}
//开始创建线程
public void start() {
this.thread.start();
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (ICommonObject.lock) {
//若numPool中是空的,说明上一次生产的数据已经被消费生产者需要继续生产,否则线程进入阻塞态。
if (!commonObject.isNumEmpty()) {
try {
ICommonObject.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
commonObject.setNum(i);
System.out.println("生产者(" + thread.getName() + ")生产了一个数据:" + i);
//生产者生产数据后,进入阻塞态,唤醒阻塞队列中的其他线程。
ICommonObject.lock.notify();
}
}
}
class InnerProducer implements ICommonObject {
public InnerProducer() {
}
}
}
消费者Consumer:
package com.mec.about_thread.test;
public class Consumer implements Runnable {
private Thread thread;
private ICommonObject commonObject;
public Consumer() {
this.commonObject = new InnerConsumer();
this.thread = new Thread(this);
}
//开始创建线程
public void start() {
this.thread.start();
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
synchronized (ICommonObject.lock) {
//如果numPool中没有数据,Customer进入阻塞态,否则继续消费
if (commonObject.isNumEmpty()) {
try {
ICommonObject.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int num = commonObject.getNum();
System.out.println("消费者消费了" + num);
//完成消费后,唤醒阻塞队列中的线程
ICommonObject.lock.notify();
}
}
}
class InnerConsumer implements ICommonObject {
public InnerConsumer() {
}
}
}
测试类Test:
package com.mec.about_thread.test;
public class Test {
public static void main(String[] args) {
Producer producer1 = new Producer("A");
Consumer consumer = new Consumer();
consumer.start();
producer1.start();
System.out.println("主线程主函数执行完毕!");
}
}
执行结果:
这里我们就很好地完成了,生产者——消费者的问题。