生产者消费者问题中的同步机制JAVA设计和实现
问题描述
若干进程通过有限的共享缓冲区交换数据。其中,X个“生产者”进程不断写入数据,而Y个“消费者”进程不断读出数据;共享缓冲区共有N个;任何时刻只能有一个进程可对公用缓冲池进行操作。
问题分析
我们已经知道,这样设计就可以保证先后顺序:对于某一个需要后进行的进程:wait(sth),wait(mutex),signal(mutex),先进行的进程:wait(mutex),signal(mutex),signal(sth)。而本问题也有先后的问题,对于消费者来讲,只有先有货物才可以消费。对于生产者来讲只有有空位才可以生产。所以需要三个信号量,两个(full和empty)用来决定先后顺序,一个是共享共用缓冲池的标志。
对于生产者(Producer):wait(empty),wait(mutex),其他操作,signal(mutex),signal(full)。
对于消费者(Consumer):wait(full),wait(mutex),其他操作,signal(mutex),signal(empty)。
利用记录型信号量解决
运行环境
Java SE 12
实现思路
使用Semaphore类。
void acquire(int permits) :获取指定数目的资源,如果无可用资源将会一直阻塞等待。相当于wait()。
void release(int permits): 释放指定数目的资源。相当于signal()。
代码实现
final int N=5;//仓库容量
private static Integer count = 0;//现有资源数目
final Semaphore empty = new Semaphore(N);//表示空的数目
final Semaphore full = new Semaphore(0);//表示满的数目
final Semaphore mutex = new Semaphore(1);
public static void main(String[] args) {
TestG test=new TestG();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
}
class Producer implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);//睡眠
} catch (Exception e) {
e.printStackTrace();
}
try {
empty.acquire(1);//wait(empty)
mutex.acquire();//wait(mutex)
count++;
System.out.println(Thread.currentThread().getName()+ "生产者正在生产,目前总共有" + count);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mutex.release();//signal(mutex)
full.release(1);//signal(full)
}
}
}
}
class Consumer implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);//睡眠
} catch (Exception e) {
e.printStackTrace();
}
try {
full.acquire(1);//wait(full)
mutex.acquire();//wait(mutex)
count--;
System.out.println(Thread.currentThread().getName()+ "消费者正在消费,目前还剩" + count);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mutex.release();//signal(mutex)
empty.release(1);//signal(empty)
}
}
}
}
运行截图

过程中出现的问题和注意点
因为Semaphore三个信号量都被声明为了final所以一旦被声明不再会改变,所以如果通过构造函数传进来N是不行的,因为在这之前Semaphore已经被声明并且无法更改。
利用AND信号集解决
运行环境
Java SE 12
实现思路
将wait(empty)和wait(mutex)、signal (mutex )和signal (full)、wait (full) 和wait (mutex )、signal (mutex )和signal (empty)结合起来。其实结合起来之后,full和empty就可以用一个标志表示了,在这里我设置为count表示资源数。用synchronized给mutex加锁实现同步。
代码实现
private static Integer count=0;//表示现有货物资源数目
private static String mutex = "mutex";//标志资源区(仓库)是不是被占用
public static void main(String[] args) {
Test test=new Test();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Producer()).start();
new Thread(test.new Consumer()).start();
}
class Producer implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (mutex) {//同步
while (count == 10) {//仓库满了
try {
mutex.wait();//释放mutex的锁
} catch (Exception e) {
e.printStackTrace();
}
}
//生产
count++;
System.out.println(Thread.currentThread().getName() + "生产者正在生产,货物共有"+count);
mutex.notifyAll();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mutex) {//同步
while (count == 0) {
try {
mutex.wait();//释放mutex
} catch (Exception e) {
}
}
count--;//消费
System.out.println(Thread.currentThread().getName() + "消费者正在消费,货物还剩"+count);
mutex.notifyAll();
}
}
}
}
运行截图

生产者消费者问题中的同步机制JAVA设计和实现的更多相关文章
- Java中的闪光点:ThreadLocal是线程Thead的局部变量,可替代同步机制的设计,值得学习和研究
线程局部变量ThreadLocal,是Java支持的一种线程安全机制,目的是解决多线程的并发问题. 具体来讲,就是多个线程访问该实例对象的变量时,该实例对象将其存储为键值对的形式,保证各个线程(键)分 ...
- 生产者消费者模式中条件判断是使用while而不是if
永远在循环(loop)里调用 wait 和 notify,不是在 If 语句现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你 ...
- python JoinableQueue在生产者消费者项目中的简单应用
class multiprocessing.JoinableQueue([maxsize]) JoinableQueue, a Queue subclass, is a queue which add ...
- Java笔记1 : 在生产者消费者模式中,线程通信与共享数据,死锁问题与解决办法
本例定义了4个类,这里说一下,方便下面讲解.分别是Product(产品),Producer(生产者),Consumer(消费者), Test(测试类). 多线程之间通信与共享数据只要引用同一内存区域就 ...
- java 中的同步机制
对于有些场景,需要a.b线程按照顺序去执行,因为b线程要依赖a线程对某共享资源或 状态处理后,对于这种情况可以使用 private CountDownLatch connectedSignal = n ...
- 对JavaScript中异步同步机制以及线程深入了解
今天在网上看到各种对Js异步同步单线程多线程的讨论 经过前辈们的洗礼 加上鄙人小小的理解 就来纸上谈兵一下吧~ Js本身就是单线程的 至于为什么Js是单线程的 那就要追溯到Js的历史了 总而言之 由于 ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例
如何使用Wait 尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单.如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题 ...
- 【总结】Java线程同步机制深刻阐述
原文:http://hxraid.iteye.com/blog/667437 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread). 线程(Thread ...
随机推荐
- 【Jenkins】参数化引用
我们在Jenkins里设置了参数如下 1. Jenkins中引用 shell引用 $env windows bat引用 %env% 在git等源码管理时,调用参数的格式${env} 2. jmete ...
- B. 复读机的力量
我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人说的话. 我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人说的话. 我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人 ...
- Git敏捷开发--常用别名
前言 在Unix下终端开发时,经常会搭配 oh-my-zsh 来使用. oh-my-zsh 中默认内置 git 插件,且支持许多 git alias 的命令,这里对常见的命令进行总结,以供查阅. 常用 ...
- Redisson 实现分布式锁的原理分析
写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题. 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...
- 从零开始学AB测试:基础篇
什么是AB测试? 通俗点理解,AB测试就是比较两个东西好坏的一套方法,这种A和B的比较在我们的生活和人生中非常常见,所以不难理解.具体到AB测试这个概念,它和我们比较哪个梨更大.比较哪个美女更漂亮.比 ...
- 9. 弹出键盘挡住input
1.) react 中 <input className="inp3" placeholder="密码" type="password" ...
- hashlib的md5计算
hashlib的md5计算 hashlib概述 涉及加密服务:Cryptographic Services 其中 hashlib是涉及 安全散列 和 消息摘要 ,提供多个不同的加密算法借口,如SHA1 ...
- redis管道操作(事务),无回滚
管道:将数据操作放在内存中,只有成功后,才会一次性全部放入redis #管道(事务),要是都成功则成功,失败一个全部失败 #原理:将数据操作放在内存中,只有成功后,才会一次性全部放入redis pip ...
- 2019-2020-1 20199308《Linux内核原理与分析》第七周作业
<Linux内核分析> 第六章 进程的描述和进程的创建 6.1 进程的描述 操作系统内核实现操作系统的三大管理功能: 进程管理(进程)-核心 内存管理(虚拟内存) 文件系统(文件) 为了管 ...
- 2019-2020-1 20199310《Linux内核原理与分析》第六周作业
1.问题描述 在前面的文章中,学习了系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用.本文将内容围绕系统调用system_call的处理过程,在Me ...