生产者消费者模式
生产线程负责生产,消费线程负责消费。
生产线程和消费线程要达到均衡。
这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。
博主最近一直在跟着杜老师的Java网课进行学习,本题将以杜老师的课堂作业作为范例对wait()和notify()方法进行讲解。
题目要求
使用生产者和消费者模式实现,交替输出:
假设只有两个线程,输出以下结果:
t1–>1
t2–>2
t1–>3
t2–>4
t1–>5
t2–>6
…
要求:必须交替,并且t1线程负责输出奇数。t2线程负责输出偶数。
两个线程共享一个数字,每个线程执行时都要对这个数字进行:++
知识储备
wait()和notify()方法是来自Object类,所以一切引用类对象均有这两种方法。
wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。
notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
思路
用生产者和消费者模式必然是两个线程对同一个对象进行操作。
所以:一、需要两个线程类,一个操作的对象类。
二、需要用synchronized关键字,避免多线程安全问题。
代码实现
package 生产者_消费者_作业;
/*
思路:用生产者和消费者模式必然是两个线程对同一个对象进行操作。
所以:一、需要两个线程类,一个操作的对象类。
二、需要用synchronized关键字,避免多线程安全问题。
*/
public class wait_notify_方法认知验证 {
public static void main(String[] args) {
// 创建对象
GongXiang gongXiang = new GongXiang();
// 创建线程的同时记得在线程中传入同一个对象
// 这样才能达到多线程对同一个对象进行操作
Thread t1 = new Thread(new MyThread1(gongXiang));
Thread t2 = new Thread(new MyThread2(gongXiang));
// 重命名线程名,便于后续输出观察
t1.setName("偶数线程");
t2.setName("奇数线程");
// 线程开始
t1.start();
t2.start();
}
}
class GongXiang {
// 根据题意,创建个int对象即可
private int i=0;
// 构造方法
public GongXiang() {
}
public GongXiang(int i) {
this.i = i;
}
// getter和setter方法
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
// 这里采用的是实现接口的方式来构建线程
// 这里的MyThread1线程类用来输出偶数
class MyThread1 implements Runnable {
// 因为需要对同一对象进行操作,所以线程中必须有该对象
private GongXiang gongXiang;
public MyThread1() {
}
// 传入对象
public MyThread1(GongXiang gongXiang) {
this.gongXiang = gongXiang;
}
// 关键点:线程开始时执行的代码区域
@Override
public void run() {
// 由于是生产者和消费者模式,所以要使用while循环,让两个线程保持平衡的方法实现在循环里,用wait()和notify()方法
while(true){
// 由于是多线程操作一个对象,必须使用synchronized避免线程安全问题
synchronized (gongXiang){
// 这里线程开始,打印数据记录
System.out.println(Thread.currentThread().getName()+"开始");
// 由于MyThread1主要输出的是偶数,所以此时如果对象中的值是奇数就wait()
if(gongXiang.getI()%2!=0)
{
try {
// 打印信息
System.out.println(Thread.currentThread().getName()+"要暂停了!");
gongXiang.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 程序能到这里说明对象中是偶数,所以输出
System.out.println(Thread.currentThread().getName()+"要输出了!");
int t = gongXiang.getI();
System.out.println(t);
// 这里暂停一秒是为了运行时能够看清些,便于后期看输出结果理解wait()和notify()方法
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 对象中的值++
gongXiang.setI(t+1);
gongXiang.notifyAll();
}
}
}
}
// 这里的MyThread2线程类用来输出奇数
// 后面的和上面的线程差不多,注释就不写了
class MyThread2 implements Runnable {
private GongXiang gongXiang;
public MyThread2() {
}
public MyThread2(GongXiang gongXiang) {
this.gongXiang = gongXiang;
}
@Override
public void run() {
while(true){
synchronized (gongXiang){
System.out.println(Thread.currentThread().getName()+"开始");
if(gongXiang.getI()%2!=1)
{
System.out.println(Thread.currentThread().getName()+"要暂停了!");
try {
gongXiang.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"要输出了!");
int t = gongXiang.getI();
System.out.println(t);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
gongXiang.setI(t+1);
gongXiang.notifyAll();
}
}
}
}
输出结果
偶数线程开始
偶数线程要输出了!
0
偶数线程开始
偶数线程要暂停了!
奇数线程开始
奇数线程要输出了!
1
奇数线程开始
奇数线程要暂停了!
偶数线程要输出了!
2
偶数线程开始
偶数线程要暂停了!
奇数线程要输出了!
3
奇数线程开始
奇数线程要暂停了!
偶数线程要输出了!
4
偶数线程开始
偶数线程要暂停了!
奇数线程要输出了!
5
奇数线程开始
奇数线程要暂停了!
偶数线程要输出了!
6
奇数线程要输出了!
7
奇数线程开始
奇数线程要暂停了!
偶数线程开始
偶数线程要输出了!
8
偶数线程开始
偶数线程要暂停了!
奇数线程要输出了!
9
奇数线程开始
奇数线程要暂停了!
偶数线程要输出了!
10
偶数线程开始
偶数线程要暂停了!
奇数线程要输出了!
11
奇数线程开始
奇数线程要暂停了!
偶数线程要输出了!
12
结果解释
![](https://i-blog.csdnimg.cn/blog_migrate/b570d2920438121ba7c84ff633b74d67.png#pic_center)
![](https://i-blog.csdnimg.cn/blog_migrate/f096619fb0101047610899eafe05616d.png#pic_center)
![](https://i-blog.csdnimg.cn/blog_migrate/00c660779222649eee50a0e2027f332e.png#pic_center)
总结
wait()方法:暂停当前线程,并释放当前线程所占用的锁,被notify()方法唤醒后,会接着上一次执行的地方执行。
notify()方法:唤醒当前对象上等待的一个线程,如果有多个,就随机唤醒一个。
notifyAll()方法:唤醒当前对象上所有等待的线程。
如有谬误,请务必告知,以免误导他人
如果各位看官觉得写得不错,对你有帮助,能不能给个赞啊,这是对我最大的鼓励!