JAVA学习第二十七课(多线程(六))- 多生产者多消费者问题(JDK1.5新特性)
多生产者多消费者问题
以生产馒头 消费馒头为例。
class Resource
{
private String name;
private int count = 1;
private boolean flag = false; public synchronized void set(String name)
{
if (flag)
{
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
flag = true;
notify();
}
public synchronized void out()
{
if (!flag) {
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(Thread.currentThread().getName()+"---------消费者--------"+this.name);
flag = false;
notify();
}
} class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("馒头");
}
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} public class Main
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
上述代码easy出现一个问题,就是同一个馒头被多次消费。或者有的馒头没有被消费。单生者,单消费者不会出现这个问题
原因:notify 唤醒的线程是随意一个,比方t0 t1都是等待状态,t0活了之后,t1也可能被唤醒,所以就会产生多个馒头而没有被消费的情况,针对这个问题。flag的推断能够改为循环,这样却easy造成了死锁情况:t0 t1被wait了。t2正常消费一次,flag = false。唤醒了t0。由于循环t2 t3被wait了。t0运行一次,flag = true。如果唤醒t1。推断t0进行wait。while,t1也进入了等待,死锁状态
PS:冻结状态的线程被唤醒后本次就不參与推断,向下运行,所以简单来说,多个馒头没有被消费问题的产生,是由于冻结状态的线程不再继续參与推断造成的,而死锁是由于循环推断造成的
多生产多消费问题解决:
notify,改为notifyAll。当前本类线程中的一个正常运行后,唤醒全部线程,当前同类线程由于while,自然会继续等待,对方随意一个线程运行一次,对方剩余线程继续等待。这样就实现了生成相应消费
class Resource
{
private String name;
private int count = 1;
private boolean flag = false; public synchronized void set(String name)
{
while (flag)
{
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者----"+this.name);
flag = true;
notifyAll();
}
public synchronized void out()
{
while (!flag) {
try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(Thread.currentThread().getName()+"---------消费者--------"+this.name);
flag = false;
notifyAll();
}
}
总结:
if推断。仅仅会推断一次。easy造成不该唤醒的线程被唤醒了,出现故障
while推断,尽管攻克了线程获取执行权后。是否要执行
notify。仅仅能唤醒随意一个线程,可是唤醒本方线程,没有意义。且while+notify = 死锁
notifyAll,攻克了本方线程一定会唤醒对方线程
所以,多生产多消费问题 = while + notifyAll,可是也造成了效率低的问题(本方不该唤醒,也被唤醒了)
新特性(JDK1.5升级)
怎样不唤醒本方。仅仅唤醒对方呢
void Fu()//前期
{
synchronized (obj) //关于锁的操作是隐式的,仅仅有锁自己知道
{
code....
}
}
//后期升级,将锁这一事物封装成了对象 Lock L = new ReentrantLock();,将操作锁的隐式方式定义到了对象中
void Fu()
{
L.lock();//获取锁
code..
L.unlock();//释放锁
}
接口Lock
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现同意更灵活的结构。能够具有区别非常大的属性。能够支持多个相关的 Condition 对象。
Condition 将 Object 监视器方法(wait、notify 和notifyAll)分解成截然不同的对象。以便通过将这些对象与随意
Lock 实现组合使用。为每一个对象提供多个等待 set(wait-set)。
当中,Lock 替代了 方法和语句的使用。
synchronizedCondition 替代了 Object 监视器方法的使用。
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
Lock接口:替代了同步代码块或同步函数。将同步锁的隐式操作变成现实操作。更为灵活,能够一个锁加多个监视器
方法:
L.lock();//获取锁
L.unlock();//释放锁。一般与finally代码块连用
Condition接口:实现了Object中wait(),notify()。notifyAll()方法,将这些监视器方法进行了单独的封装,变成Condition监视器对象。能够随意锁进行组合
await() 相当于 wait
signal() 相当于 notify
signalAll(); 相当于 notifyAll
import java.util.concurrent.locks.*; class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
//创建一个锁
Lock L = new ReentrantLock();
//通过已有的锁,获取该锁监视器对象(Condition)
//Condition con = L.newCondition();
//通过已有的锁,获取两组监视器。一个监视生产者,一个监视消费者
Condition pro_con = L.newCondition();
Condition consu_con = L.newCondition(); public void set(String name)
{
L.lock();
try {
while (flag)
{
try {
pro_con.await();//线程冻结
}catch (Exception e) {
// TODO: handle exception
}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"---生产者5.0----"+this.name);
flag = true;
//con.signalAll();//唤醒全部线程
consu_con.signal();//唤醒消费者线程,不用All
} catch (Exception e) {
// TODO: handle exception
}
finally
{
L.unlock();//释放锁
} }
public void out()
{
L.lock();
try {
while (!flag) {
try {
consu_con.await();
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(Thread.currentThread().getName()+"---------消费者5.0--------"+this.name);
flag = false;
//con.signalAll();
pro_con.signal();
} catch (Exception e) {
// TODO: handle exception
}
finally
{
L.unlock();
} }
} class Producer implements Runnable
{
private Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("馒头");
}
}
}
class Consumer implements Runnable
{
private Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} public class Main
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
实际开发是这种代码
class BoundedBuffer {//缓冲区
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];//创建一个大的容器
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
JAVA学习第二十七课(多线程(六))- 多生产者多消费者问题(JDK1.5新特性)的更多相关文章
- JAVA学习第二十一课(多线程(一)) - (初步了解)
放假在家,歇了好几天了,也没学习,今天学习一下多线程.找找感觉.后天就要回学校了.sad... PS:包 没有什么技术含量,会用就可以,日后开发就必需要会用啦,所以打算先放一放,先来多线程 一.多线程 ...
- Python学习第二十七课——写一个和Django框架的自己的框架
MyWeb框架: from wsgiref.simple_server import make_server def application(environ, start_response): pri ...
- 风炫安全WEB安全学习第二十七节课 XSS的防御措施
风炫安全WEB安全学习第二十七节课 XSS的防御措施 XSS防御措施 总的原则 控制好输入/输出 过滤:根据业务需求进行过滤,对email,手机号码这样的输入框进行验证. 转义:所有输出到前端的数据都 ...
- NeHe OpenGL教程 第二十七课:影子
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- 风炫安全WEB安全学习第二十二节课 DOM型XSS讲解
风炫安全WEB安全学习第二十二节课 DOM型XSS讲解 Dom型XSS演示 通过Javascript,可以重构整个HTML文档,你可以添加.移除.改变或重排页面上的项目 要改变页面的某个东西,Java ...
- 【MQ】java 从零开始实现消息队列 mq-02-如何实现生产者调用消费者?
前景回顾 上一节我们学习了如何实现基于 netty 客服端和服务端的启动. [mq]从零开始实现 mq-01-生产者.消费者启动 [mq]java 从零开始实现消息队列 mq-02-如何实现生产者调用 ...
- 201671010140. 2016-2017-2 《Java程序设计》java学习第二周
学习第二周(Java基本程序设计结构) 这一周,着重学习了Java的简单程序设计实现及运行,通过自己操作,发现Java的程序语法大面 ...
- 2017年1月1日 java学习第二天复习
今天是新年的第一天,以前学习没有总结习惯,学习效率和成果都很不好. 学习的过程就是反复的复习和不断学习的过程,开始今天的学习总结 学习java的第二天. 今天学习了java最基础的一些内容,照着 ...
- Java学习第二周学习笔记
20145307<Java程序设计>第二周学习总结 教材学习内容总结 Java语言中的很多基本语法都和C语言类似,以下Java中的基本语法 标识符 标识符是程序中自定义的一些名称. 由26 ...
随机推荐
- Python 绘图与可视化 matplotlib 填充fill和fill_between
参考链接:https://blog.csdn.net/You_are_my_dream/article/details/53457960 fill()填充函数曲线与坐标轴之间的区域: x = np.l ...
- [luogu] P4823 [TJOI2013]拯救小矮人(贪心)
P4823 [TJOI2013]拯救小矮人 题目描述 一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯.即:一个小矮人站在另一小矮人的 肩膀上,知道最顶端的小矮人伸直胳膊可以 ...
- UIScrollView加入控件,控件距离顶部始终有间距的问题
今天.特别郁闷.自己定义了一个UIScrollView,然后在它里面加入控件,如UIButton *button = [[UIButton alloc] initWithFrame:CGRectMak ...
- 剑指offer_面试题_从上往下打印二叉树
题目:从上往下打印出二叉树的每一个结点.同一层的结点依照从左到右的顺序打印.比如输入图4.5中的二叉树.则依次打印出8.6.10.5.7.9.11. 8 / \ 6 10 / \ ...
- Android解决ScrollView视图导致其底部的布局栏被推到上边的问题
近期有个xml布局文件,我说下大概意思: <ScrollView> ...... </ScrollView> <RelativeLayout> ...... < ...
- Zorka监控平台的Online reconfiguration基本效果展示
在上一篇日志中,我简介了Zorka的Online reconfiguration的用法,可是没怎么介绍如何看到在线更改的效果,这里简单说说. 还是以之前的tomcat为例,我们在文件夹zorka\sc ...
- validation-api参数校验
这里针对springboot项目结构 maven添加: <dependency> <groupId>javax.validation</groupId> <a ...
- 解决win8.1下sql配置iis的问题
在配置iis8.5时,ISAPI和CGI限制中没有ASP.NET v4.0.30319, 所以要注册.net 4.0 注册方法为在“运行”中输入cmd,然后在命令行中输入: C:\WINDOWS\Mi ...
- Codeforces 987C. Three displays(o(n^2))
刚开始三重循环tle test11.后来想了个双重循环的方法. 解题思路: 1.双重循环一次,用一个一位数组存j和比j小的i的和的最小值. 2.再双重循环一次,找到比j大的数k,更新结果为ans=mi ...
- 优动漫PAINT基础系列之存储格式说明
本篇经验带大家了解优动漫PAINT可以存储成哪些格式! 最近有收到试用优动漫PAINT个人版试用版的小伙伴提问,优动漫PAINT可以导出什么格式文件呢?今天就这一问题做一下解答〜 优动漫PAINT[试 ...