java:多线程_生产者消费者模式_wait()和notify()方法理解与使用。

生产者消费者模式

  生产线程负责生产,消费线程负责消费。
  生产线程和消费线程要达到均衡。
  这是一种特殊的业务需求,在这种特殊的情况下需要使用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

结果解释

总结

  wait()方法:暂停当前线程,并释放当前线程所占用的锁,被notify()方法唤醒后,会接着上一次执行的地方执行。
  notify()方法:唤醒当前对象上等待的一个线程,如果有多个,就随机唤醒一个。
  notifyAll()方法:唤醒当前对象上所有等待的线程。

如有谬误,请务必告知,以免误导他人
如果各位看官觉得写得不错,对你有帮助,能不能给个赞啊,这是对我最大的鼓励!

猜你喜欢

转载自blog.csdn.net/weixin_51304266/article/details/117379530