基于Java Swing 可视化模拟生产者与消费者问题(代码开源)

基于Java Swing 可视化模拟生产者与消费者问题


前言

本文介绍计算机系统进程同步中经典的生产者与消费者问题,同时通过代码在控制台模拟了整个过程。最后基于Java Swing 开发项目,可视化模拟消费者与生产者问题。


一、什么是进程同步?

提到生产者与消费者问题,就不得不提到进程同步,那么进程同步的作用是什么呢?

:简单来说,进程同步让并发执行的多个进程之间按照一定的规则,共享系统资源,使程序的执行具有可再现性。


二、什么是生产者与消费者问题?

生产者-消费者问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产产品,并将这些产品提供给消费者进程进行消费。

为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓存区的缓冲池(本项目n = 1),生产者进程将其所生产的产品放入其中一个缓存区中;消费者进程可从一个缓存区中取走产品区消费。

尽管所有的生产者进程与消费者进程都是以异步方式运行的,但它们之间必须保持同步,既不允许消费者进程到一个空缓存区区取产品,也不允许生产者进程向一个已装满产品且尚未被去凑的缓存区中投放产品。


三、Java关键代码演示与讲解

1. 运行演示

创建三个生产者、三个消费者进程。

结果演示

2. 生产者类(Producer)

代码如下:


	private Buffer buffer;
    public boolean isPause = false;// 控制生产者状态(生产/暂停)

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
                if(!isPause){
                    // 缓存区商品+1
                    buffer.produceAGood();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

3. 消费者类(Consumer)

代码如下:


	public boolean isPause = false;// 控制消费者状态(消费/暂停)
    public Buffer buffer;

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(3000);
                if(!isPause){
                    // 缓存区商品-1
                    buffer.consumeAGood();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

4. 缓存区类(Buffer)

代码如下:

	// 缓存区最大商品数
    private static final int MAX = 10;
    
    // 当前缓存区商品的个数
    private int GoodNum = 0;

    // 判断缓存区是否已经满了
    public boolean isFull = false;

    // 判断缓存区是否为空
    public boolean isEmpty = true;

    // 锁
    private final Lock lock = new ReentrantLock();

    // 缓存区满的条件变量
    private final Condition full = lock.newCondition();
    
    // 缓存区空的条件变量
    private final Condition empty = lock.newCondition();

    // 生产者生产商品
    public void produceAGood(){
        // 获取锁,同一个时间只能有一个生产者生产商品
        lock.lock();
			
		// 如果当前缓存区一直为满
        while (isFull){
            try {
            	// 将该进程置为等待
                full.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // 开始生产
        GoodNum += 1;
        if(GoodNum == MAX){
            isFull = true;
        }

        // 如果生产之前,缓存区为空
        if(isEmpty){
            isEmpty = false;

            // 唤醒消费者进程
            empty.signalAll();
        }

        System.out.println(Thread.currentThread().getName()+" 生产了第 " + (GoodNum) + " 个商品....");

        // 释放锁
        lock.unlock();
    }

    // 消费者消费商品
    public void consumeAGood(){
        // 获得锁,同一个时间只能有一个消费者消费商品
        lock.lock();
        
		// 如果当前缓存区一直为空
        while (isEmpty){
            try {
            	// 将该进程置为等待
                empty.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // 开始消费
        GoodNum -= 1;
        if(GoodNum == 0){
            isEmpty = true;
        }

        // 如果消费之前缓存区已满
        if(isFull){
            isFull = false;
            // 唤醒生产者进程
            full.signalAll();
        }

        System.out.println(Thread.currentThread().getName()+" 消费了第 " + (GoodNum+1) + " 个商品....");

        // 释放锁
        lock.unlock();
    }

关键代码讲解:

	// 创建一个static锁,保证该类不同线程实例能够感知到signalAll()
    private final Lock lock = new ReentrantLock();

    // 缓存区满的条件变量。await() 与 signalAll() 是基于此实现的。
    private final Condition full = lock.newCondition();
    
    // 缓存区空的条件变量
    private final Condition empty = lock.newCondition();
    
    // 举例生产者进程讲解 await() 与 signalAll() 的使用。
    public void produceAGood(){
        // 获取锁,同一个时间只能有一个生产者生产商品
        lock.lock();
			
		// 如果当前缓存区一直为满
        while (isFull){
            	// 将该进程置为等待
                full.await();
        }

        // 开始生产
        GoodNum += 1;

        // 如果生产之前,缓存区为空,则说明可能有消费者进程等待被唤醒。
        if(isEmpty){
            // 唤醒消费者进程
            empty.signalAll();
			isEmpty = false;
        }

        System.out.println(Thread.currentThread().getName()+" 生产了第 " + (GoodNum) + " 个商品....");

        // 该生产者完成该次生产,需释放锁
        lock.unlock();
    }

思路成立,开始实现!


四、界面演示与讲解

1. 界面演示

程序运行演示

2. 界面讲解

(1)生产者生产商品的过程抽象为进度条的加载过程,当进度条加载完成,则表示生产者完成一次商品的生产。

Producer 类关键代码如下:

public class Producer implements Runnable{

    private  int MIN_PROGRESS = 0;
    private  int MAX_PROGRESS = 100;
    private  int currentProgress = MIN_PROGRESS;
    public boolean isPause = true;

	@Override
    public void run() {
        while(true){
            try {
                Thread.sleep(produceSpeed);
                // 该生产者处于生产状态
                if(!isPause){
                	// 设置进度条对应的值
                    currentProgress++;
                    // 当生产者完成一次生产时
                    if (currentProgress >= MAX_PROGRESS) {
                        // 缓存区商品+1
                        buffer.produceAGood();
                        // 重新开始生产一个新的商品
                        currentProgress = 0;
                    }else {
                        // 采用进度条显示生产商品的过程
                        myProgressBar.getProgressBar().setValue(currentProgress);
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

(2)消费者消费商品的过程抽象为当Waiter图片(结合上面gif)移动到指定位置时,消费者发起消费一次商品的生产。

Consumer 类关键代码如下:

public class Consumer implements Runnable{
	// 控制消费者状态(消费/暂停)
    public boolean isPause = true;

    public Buffer buffer;

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(consumeSpeed);
                if(!isPause){
                    picPosY--;
                    // 消费者完成一次消费
                    if (picPosY <= DEADLINE) {
                        // 缓存区商品-1
                        buffer.consumeAGood();
                        // 重置消费者位置(消费者开启一次新的消费)
                        picPosY = 550;
                    }
                    // 更新消费者的位置
                    listeners.firePropertyChange(Thread.currentThread().getName(),picPosY+1,picPosY);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

五、项目地址

项目托管在Gitee上,为开源项目,可供学习参考与非商用使用,侵权必究。
模拟生产者与消费者过程


六、总结

通过该项目,对进程同步相关的概念以及同步机制有了进一步的了解,同时锻炼了简单程序的开发能力。


  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的生产者消费者问题代码实现,使用 Python 语言实现: ```python import threading import time # 定义一个共享资源,表示生产者消费者共同操作的数据 shared_resource = [] # 定义一个互斥锁,保证共享资源的安全访问 mutex = threading.Lock() # 定义一个信号量,表示生产者可以生产的数量 semaphore = threading.Semaphore(10) # 定义一个生产者线程类 class ProducerThread(threading.Thread): def run(self): global shared_resource global mutex global semaphore while True: # 获取信号量,如果已经达到最大数量,则阻塞等待 semaphore.acquire() # 加锁,保证共享资源的安全修改 mutex.acquire() # 生产一个数据,并加入共享资源中 shared_resource.append(1) print("Producer produced 1, shared resource: ", shared_resource) # 释放锁,让其他线程可以访问共享资源 mutex.release() # 休眠一段时间,模拟生产过程 time.sleep(1) # 定义一个消费者线程类 class ConsumerThread(threading.Thread): def run(self): global shared_resource global mutex global semaphore while True: # 加锁,保证共享资源的安全访问 mutex.acquire() if len(shared_resource) > 0: # 消费一个数据,并从共享资源中移除 shared_resource.pop() print("Consumer consumed 1, shared resource: ", shared_resource) # 释放锁,让其他线程可以访问共享资源 mutex.release() # 释放信号量,表示可以继续生产 semaphore.release() else: # 如果共享资源为空,则释放锁,让其他线程可以访问共享资源,并阻塞等待 mutex.release() time.sleep(1) # 创建一个生产者线程和三个消费者线程 producer_thread = ProducerThread() consumer_thread1 = ConsumerThread() consumer_thread2 = ConsumerThread() consumer_thread3 = ConsumerThread() # 启动线程 producer_thread.start() consumer_thread1.start() consumer_thread2.start() consumer_thread3.start() ``` 上述代码通过创建一个共享资源列表 `shared_resource`,一个互斥锁 `mutex`,一个信号量 `semaphore`,以及一个生产者线程和三个消费者线程来模拟生产者消费者问题生产者线程通过不断地获取信号量 `semaphore`,加锁 `mutex`,向共享资源列表中添加一个数据,并释放锁 `mutex`,然后休眠一段时间,模拟生产过程。消费者线程通过加锁 `mutex`,判断共享资源列表中是否有数据可供消费,如果有则消费一个数据,并释放锁 `mutex`,然后释放信号量 `semaphore`,表示可以继续生产;如果共享资源列表为空则释放锁 `mutex`,然后阻塞等待。 通过这种方式,生产者消费者可以安全地访问共享资源,而不会发生竞争条件或数据不一致的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值