Java 多线程开发 06 —— 管程法、信号灯法

本文介绍了Java多线程开发中的生产者消费者问题,阐述了如何利用管程法和信号灯法解决线程间的通信与同步问题。通过示例代码展示了如何在缓冲区满或空的情况下,协调生产者和消费者的执行,确保数据的正确生产和消费。
摘要由CSDN通过智能技术生成

系列文章目录

Java 多线程开发 01 —— 线程创建
Java 多线程开发 02 —— 静态代理模式
Java 多线程开发 03 —— Lambda表达式
Java 多线程开发 04 —— 线程状态控制、优先级、守护线程
Java 多线程开发 05 —— synchronized、Lock、死锁
Java 多线程开发 06 —— 管程法、信号灯法



生产者、消费者问题

应用场景:

  1. 假设仓库只能放一件产品,生产者放入一件产品到仓库,消费者从仓取出一件产品。
  2. 若仓库有一件产品,则生产者必须等待消费者取出一件。
  3. 若仓库没有产品,则消费者必须等待生产者放入一件产品。

这里就涉及到一个线程通信的问题。

  1. 对于生产者,在生产了一件产品后要通知消费者取走。
  2. 对于消费者,在取走了一件产品后要通知生产者生产。

在这个问题中,仅用synchronized是不够的,它不能实现不同线程之间的消息传递。

Java 提供了几个方法来解决线程之间的通信问题:

方法名作用
wait()表示线程一直等待,直到有其他线程通知(与sleep()不同,可以释放锁)
wait(long timeout)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级高的优先调度

注意:这些都是Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常。

管程法

生产者将生产好的数据放入缓冲区,消费者冲缓冲区中拿出数据,缓冲区同一时刻只能被一个人操作。

package lessen08_Thread;

//测试生产者消费者模型 —— 管程法(利用缓冲区)
public class TestPC01 {
    public static void main(String[] args) {
        Buffer buffer = new Buffer();
        new Thread(new Producer(buffer)).start();
        new Thread(new Consumer(buffer)).start();
    }
}

//生产者
class Producer implements Runnable{
    Buffer buffer = null;

    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            buffer.push(new Product(i));
            System.out.println("生产——>"+i);
        }
    }
}

//消费者
class Consumer implements Runnable{
    Buffer buffer = null;

    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费——>"+buffer.pop().getId());
        }
    }
}

//产品
class Product{
    private int id;

    public Product(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }
}

//缓冲区
class Buffer{
    private Product[] products = new Product[10];//存放产品
    private int count = 0;//当前缓冲区产品数量
    //放入产品[对缓冲区的操作涉及并发操作,可同步方法、同步代码块、lock]
    public synchronized void push(Product product){
        //若缓冲区满,则生产者需要等待取走
        if (count >= products.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products[count++] = product;//生产
        //此时生产者生产了产品,通知消费者消费
        this.notifyAll();
    }

    //取走产品
    public synchronized Product pop() {
        //若缓冲区空,则消费者需等待生产者生产
        if(count <= 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Product product = products[--count];//消费
        //此时消费者已经消费了一个产品,通知生产者生产
        this.notifyAll();
        return product;
    }
}

部分结果:

生产——>99
消费——>96
消费——>99
消费——>98
消费——>97
消费——>95
消费——>94
消费——>93
消费——>92

信号灯法

通过标志位来判断是产者、消费者是等待还执行。

package lessen08_Thread;

//测试生产者消费者模型 ———— 信号灯法(利用标志位)
public class TestPC02 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Thread(new Watcher(tv)).start();
        new Thread(new Player(tv)).start();
    }
}

//生产者————演员
class Player implements Runnable{

    TV tv = null;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            tv.play("节目:"+i);
        }
    }
}

//消费者————观众
class Watcher implements Runnable{
    TV tv = null;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            tv.watch();
        }
    }
}

//产品————节目
class TV{
    String program;//表演的节目
    //true 演员表演,观众等待
    //false 观众观看,演员等待
    boolean flag = true;

    //表演
    public synchronized void play(String program){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("演员表演了:"+program);
        //演员表演好了,通知观众观看
        this.notifyAll();
        this.program = program;
        this.flag = false;//标志位反转

    }

    //观看
    public synchronized void watch(){
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("观众观看了:"+program);
        //观众看完了,通知演员表演
        this.notifyAll();
        this.flag = true;
    }
}

部分结果:

演员表演了:节目:0
观众观看了:节目:0
演员表演了:节目:1
观众观看了:节目:1
演员表演了:节目:2
观众观看了:节目:2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老板来碗小面加蛋~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值