多线程 - 生产者与消费者

经典面试题

数据容器:封装生产、消费所需的数据。
生产者:从容器中填装东西,东西生产好了,及时通知消费者。
消费者:从容器取数据,东西取完了,及时通知生存者生产。

分析

没有任何实战价值,但是面试的时候会问。

本文主要是想介绍 Thread 相关的函数,一般不会用到这么底层的代码。

推荐方案:使用读写锁,或者直接使用阻塞队列

Wait、Notify、NotifyAll

主要就介绍这三个方法,本案例也就用到他们:


如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
对象叫Container,A线程不再使用Container,那就等待


如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
A线程等待了,但是得通知某一个等待的线程来使用Container


如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
通知所有在等待的线程

其它方法也顺带提一下:

  • join():加入其它线程,并且等待join的线程执行完成,再执行自己的
  • join(millis):等待被join的线程的时间最长为millis毫秒。如果在millis毫秒内被join的线程还没有执行结束,则不再等待。
  • yield():给优先级高或者相同的线程让步
  • setDaemon():将普通线程变为后台线程,直接使用方法即可。
数据容器:
public class Container {
    private int count = 0;
    private Vector<String> vector = new Vector<>();
    private static final Container CONTAINER = new Container();

    private Container() {
    }

    public static Container getContainer() {
        return CONTAINER;
    }

    public synchronized void put() {
        //通知其它线程
        this.notifyAll();
        String name = Thread.currentThread().getName();
        while (vector.size() > 10) {
            System.out.println("container is filled...");
            try {
                //如果集合内容超过10等待
                this.wait();
                System.out.println("###" + name + "等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count++;
        vector.add("Milk:" + count);
        System.out.println("###" + name + "生产一个产品");
    }

    public synchronized String get() {
        //通知其它线程
        this.notifyAll();
        String name = Thread.currentThread().getName();
        String milk = null;
        while (vector.size() <= 0) {
            System.out.println("container is empty...");
            try {
                //如果集合内容为0等待
                this.wait();
                System.out.println("---" + name + "等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        milk = vector.get(0);
        vector.remove(0);
        System.out.println("---" + name + "消费一个产品");
            return milk;
    }
}
生产者
public class Fatory extends Thread {
    private Container container = Container.getContainer();
    private boolean threadStop = false;

    public Fatory(String name) {
        super(name);
        this.start();
    }

    @Override
    public void run(){
        while (!threadStop) {
            try {
                container.put();
                Thread.sleep(300);
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    }

    public void threadStop(){
        this.threadStop=true;
    }
}
消费者:
public class Shop extends Thread {
    private Container container = Container.getContainer();
    private boolean threadStop = false;

    public Shop(String name) {
        super(name);
        this.start();
    }

    @Override
    public void run() {
        while (!threadStop) {
            try {
                container.get();
                Thread.sleep(800);
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    }

    public void threadStop() {
        this.threadStop = true;
    }
}

测试方法:
public class Test {
    public static void main(String[] args) {
        new Fatory("factoryA");
        new Fatory("factoryB");
        new Shop("shopA");
        new Shop("shopB");
        new Shop("shopC");
    }
}

posted on   疯狂的妞妞  阅读(151)  评论(0编辑  收藏  举报

(评论功能已被禁用)
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示