1.案例分析
生产者消费者问题,包含了两类线程:
- 一类是生产者线程用于生产数据
- 一类是消费者线程用于消费数据
可以让生产者与消费者共享同一个数据区域(类似一个仓库,容量为1)。
生产者生产数据后放入数据共享区,并不需要知道消费者做的事情。
消费者从数据共享区获取数据,也并不需要知道生产者做的事情。
2.案例实现
容器类(Box):提供存储数据和获取数据的操作
生产者类(Producer):实现Runnable接口,重写run()方法,调用存储数据的操作
消费者类(Customer):实现Runnable接口,重写run()方法,调用获取数据的操作
测试类(Test)
2.1 Box类
public class Box{
//定义state用来判断数据共享区是否有数据
private boolean state=false;
//定义取数据方法
public synchronized void put(){
//判断数据共享区是否有数据
if (state){
try {
//有数据则进行等待,直到消费者进程取出数据进程并唤醒该进程
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行到此处,说明进程被唤醒(此时数据共享区无数据)
//用输出语句模拟数据的生产过程
System.out.println("生产者已生产出需要的数据,可以进行使用!");
//数据生产完成,修改state,表示数据共享区有数据
state=true;
//唤醒消费者线程消费数据
notifyAll();
}
public synchronized void get(){
if (!state){
try {
//数据共享区无数据,进程等待,直到生产者线程生产数据并唤醒该进程
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//执行到此处,说明进程被唤醒(此时数据共享区有数据)
//用输出语句模拟数据的取出过程
System.out.println("数据已被取出使用,请尽快生产数据!");
//数据取出,修改state,表示数据共享区无数据
state=false;
//唤醒生产者线程生产数据
notifyAll();
}
}
2.2 Producer(生产者)类
public class Producer implements Runnable{
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
b.put();
}
}
}
2.3 Customer(消费者)类
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
b.get();
}
}
}
2.4 Test(测试)类
public class Test {
public static void main(String[] args) {
//创建Box对象,这是数据共享区
Box b=new Box();
//创建生产者对象,把Box对象作为构造方法的参数传递进去
//在生产者类中要调用生产数据的操作
Producer p=new Producer(b);
//创建消费者对象,把Box对象作为构造方法的参数传递进去
//在消费者类中要调用使用数据的操作
Customer c=new Customer(b);
//创建两个线程对象,分别把生产者对象和消费者对象作为构造方法的参数传递进去
Thread t1=new Thread(p);
Thread t2=new Thread(c);
//启动线程
t1.start();
t2.start();
}
}