`
jinnianshilongnian
  • 浏览: 21433827 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2404932
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997590
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631404
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257542
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593136
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248958
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847466
Group-logo
跟我学Nginx+Lua开...
浏览量:698110
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780420
社区版块
存档分类
最新评论

多线程程序一段问题代码分析(生产者消费者)

阅读更多

问题在《一个java生产者消费者代码的问题》,解答一下,只解释原因,其他不考虑。

 

作者要的是一个生产者生成,接着必须有一个消费者消费,那这不是需要单线程吗?或者使用1个大小的阻塞队列。所以只谈论问题本身,不谈论好不好。

 

具体代码:

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;


//生产/消费者模式

public class Basket {

    Lock lock = new ReentrantLock();


    // 产生Condition对象

    Condition produced = lock.newCondition();

    Condition consumed = lock.newCondition();

    boolean available = false;


    public void produce() throws InterruptedException {

        lock.lock();


        try {

            if (available) {

                produced.await(); // 放弃lock进入睡眠

            }


            System.out.println("Apple produced.");


            available = true;


            consumed.signal(); // 发信号唤醒等待这个Condition的线程

        } finally {

            lock.unlock();

        }

    }


    public void consume() throws InterruptedException {

        lock.lock();


        try {

            if (!available) {

                consumed.await(); // 放弃lock进入睡眠

            }


            /* 吃苹果 */

            System.out.println("Apple consumed.");


            available = false;


            produced.signal(); // 发信号唤醒等待这个Condition的线程

        } finally {

            lock.unlock();

        }

    }

}

 

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//测试用类
public class ConditionTester {

    public static void main(String[] args) throws InterruptedException {
        final Basket basket = new Basket();

        // 定义一个producer
        Runnable producer = new Runnable() {
            public void run() {
                try {
                    basket.produce();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        };

        // 定义一个consumer
        Runnable consumer = new Runnable() {
            public void run() {
                try {
                    basket.consume();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        };

        // 各产生10个consumer和producer
        ExecutorService service = Executors.newCachedThreadPool();

        for (int i = 0; i < 4; i++)
            service.submit(consumer);

        Thread.sleep(2000 * 2);

        for (int i = 0; i < 4; i++)
            service.submit(producer);

        service.shutdown();
    }
}

 

原因分析:

1、假设前面有2个producer(此时available=true)

1.1、一个在等待lock

1.2、一个await

2、consumer生成内容后,available=false,produced.signal(); 最后lock.unlock();

3.1、因为lock.unlock所以会触发一个lock获取到锁(虽然signal也会触发等待这个条件的其他线程,但是多线程大家都知道什么时候触发这是不确定的),如果此时正好是[1.1]那么因为available=false,执行完释放锁

3.2、produced.signal()所以会触发一个await的producer;

 

解决方案:

只要保证[3.1]还是需要await即可解决问题

 

所以加一个 AtomicInteger producedAwaitCounter = new AtomicInteger(0); 统计当前等待的生产者,如果当前available=false,但已经有生产者生成了内容,那么先等待消费者消费了再说

 

            if (available || producedAwaitCounter.get() > 0) {

                producedAwaitCounter.incrementAndGet();

                produced.await(); // 放弃lock进入睡眠

                producedAwaitCounter.decrementAndGet();

            }

 

当然最简单的是使用:自旋,原理可以自己分析下:

            while (available) {

                produced.await(); // 放弃lock进入睡眠

            }

 

 

package com.sishuok.es.test;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;


//生产/消费者模式

public class Basket {

    Lock lock = new ReentrantLock(true);


// 产生Condition对象

    Condition produced = lock.newCondition();

    Condition consumed = lock.newCondition();

    boolean available = false;
    AtomicInteger producedAwaitCounter = new AtomicInteger(0);


    public void produce() throws InterruptedException {

        lock.lock();

        try {

            if (available || producedAwaitCounter.get() > 0) {
                producedAwaitCounter.incrementAndGet();
                produced.await(); // 放弃lock进入睡眠
                producedAwaitCounter.decrementAndGet();
            }

            System.out.println("Apple produced.");


            available = true;


            consumed.signal(); // 发信号唤醒等待这个Condition的线程

        } finally {
            lock.unlock();
        }

    }


    public void consume() throws InterruptedException {

        lock.lock();


        try {

            if (!available) {
                consumed.await(); // 放弃lock进入睡眠
            }

            /* 吃苹果 */

            System.out.println("Apple consumed.");


            available = false;

            produced.signal(); // 发信号唤醒等待这个Condition的线程
        } finally {
            lock.unlock();
        }

    }

}

 

在回答里还一个类似的,不过还是不太一样。

http://blog.csdn.net/liguogangde/article/details/9103501

 

3
2
分享到:
评论
21 楼 hui_windows 2013-09-24  
如果用这种方法,不能解决虚假唤醒问题吧?
if (available || producedAwaitCounter.get() > 0) {
            producedAwaitCounter.incrementAndGet();
            produced.await(); // 放弃lock进入睡眠
            producedAwaitCounter.decrementAndGet();
}

使用while(available ){
       produced.await(); // 放弃lock进入睡眠
    }
这种方法才是王道呀~
20 楼 lvwenwen 2013-06-26  
你的代码解决了原作者代码会卡死的问题。
19 楼 406657836 2013-06-25  
jinnianshilongnian 写道
406657836 写道
jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

开涛人气旺呀!帖子都上到首页了第一位了。给我的那篇博客都带去几十个人。牛x!

哈哈,来iteye开博客啊,一起进步

好,把博客移到iteye上来,多多指导!
18 楼 jinnianshilongnian 2013-06-25  
406657836 写道
jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

开涛人气旺呀!帖子都上到首页了第一位了。给我的那篇博客都带去几十个人。牛x!

哈哈,来iteye开博客啊,一起进步
17 楼 406657836 2013-06-25  
jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

开涛人气旺呀!帖子都上到首页了第一位了。给我的那篇博客都带去几十个人。牛x!
16 楼 406657836 2013-06-25  
jinnianshilongnian 写道
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。

我是最不知名那个,哈哈!
15 楼 jinnianshilongnian 2013-06-25  
406657836 写道
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!

李国刚,人物名。知名人物主要有洛阳市副市长,财经作家、联想管理模式研究专家等。
14 楼 406657836 2013-06-25  
teasp 写道
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?

嗯!
13 楼 teasp 2013-06-25  
406657836 写道
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!

你是liguogang?
12 楼 406657836 2013-06-25  
teasp 写道
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。

正解,呵呵!这个程序光用看确实很容易犯错。特别还是看别人的代码!以后多一起讨论下!
11 楼 teasp 2013-06-25  
对于卡死的说法,我得更正下,这是我分析错了,虽然两个producer会同时进入到await状态,但是consumer会将其中的一个唤醒。因此不会卡死。
10 楼 jinnianshilongnian 2013-06-25  
406657836 写道
可能是我表述不达意!我说的和你这个情况是一样的,只是解决方案略有不同!

是的。问题是出于一种情况。
9 楼 406657836 2013-06-25  
可能是我表述不达意!我说的和你这个情况是一样的,只是解决方案略有不同!
8 楼 teasp 2013-06-25  
我又错了,其实你的代码不能解决卡死的问题。两个producer或者consumer仍然能同时await。
7 楼 teasp 2013-06-25  
jinnianshilongnian 写道
teasp 写道
也没有完全解决卡死的问题。必须要有两个counter呀,producer一个,consumer也要一个的。

很希望听详解,具体分析下。

跟你在producer上加counter是一样的道理呀。
6 楼 teasp 2013-06-25  
jinnianshilongnian 写道
teasp 写道
虽然consumer或者producer signal另外的线程之后,没法保证下一个获取锁的到底是consumer还是producer,但是available变量可以保证如果下一个获取锁的跟signal的那个是同样类型的(都是producer或者consumer),那么它会进入wait状态。

问题就处在available,如果是unlock后又一个lock的producer获取了,因为available=false,所以不会await,但是await的producer 已经判断过了,所以不会有问题;其实可以参考第二篇帖子;但是不一样的是此处不能再简单的if,否则不会进行System.out,造成一次丢失生产者生产的数据

在await后,需要while 判断下available也能搞定。即自旋判断available直到其真正的获取到条件。

即最简单的是使用:
            while (available) {
                produced.await(); // 放弃lock进入睡眠
            }

你的说法有道理。
5 楼 jinnianshilongnian 2013-06-25  
teasp 写道
也没有完全解决卡死的问题。必须要有两个counter呀,producer一个,consumer也要一个的。

很希望听详解,具体分析下。
4 楼 jinnianshilongnian 2013-06-25  
teasp 写道
虽然consumer或者producer signal另外的线程之后,没法保证下一个获取锁的到底是consumer还是producer,但是available变量可以保证如果下一个获取锁的跟signal的那个是同样类型的(都是producer或者consumer),那么它会进入wait状态。

问题就处在available,如果是unlock后又一个lock的producer获取了,因为available=false,所以不会await,但是await的producer 已经判断过了,所以不会有问题;其实可以参考第二篇帖子;但是不一样的是此处不能再简单的if,否则不会进行System.out,造成一次丢失生产者生产的数据

在await后,需要while 判断下available也能搞定。即自旋判断available直到其真正的获取到条件。

即最简单的是使用:
            while (available) {
                produced.await(); // 放弃lock进入睡眠
            }
3 楼 teasp 2013-06-25  
也没有完全解决卡死的问题。必须要有两个counter呀,producer一个,consumer也要一个的。
2 楼 teasp 2013-06-25  
你的代码解决了原作者代码会卡死的问题。

相关推荐

    多线程代码 经典线程同步互斥问题 生产者消费者问题

    a: 创建一个线程 ...h: problem1 生产者消费者问题 (1生产者 1消费者 1缓冲区) problem1 more 生产者消费者问题 (1生产者 2消费者 4缓冲区) problem2 读者与写着问题 I: 信号量 semaphore 解决线程同步问题

    操作系统课程设计生产者和消费者问题源代码

    第一行说明程序中设置几个临界区,其余每行分别描述了一个生产者或者消费者线程的信息。每一行的各字段间用Tab键隔开。不管是消费者还是生产者,都有一个对应的线程号,即每一行开始字段那个整数。第二个字段用字母...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、...

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、...

    Java中的多线程你只要看这一篇就够了

    说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。很多人都对其中的一些...

    Java并发编程(学习笔记).xmind

    (1)如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率 (2)建模简单:通过使用线程可以讲复杂并且异步的工作流进一步分解成一组简单并且同步的工作流,每个工作流在一个单独的线程...

    javaSE代码实例

    13.2.1 一段令人困惑的字符串程序 248 13.2.2 “一次投入,终身回报”的String内存机制 249 13.2.3 String对象特殊机制付出的代价 252 13.3 StringBuffer类 253 13.3.1 弥补String不足的StringBuffer类 ...

    java jdk实列宝典 光盘源代码

    生产者、消费者问题;线程的优先级;列出虚拟机中所有的线程;守护线程Daemon;线程池;一个死锁的例子; 定时器Timer:包括在指定时间执行任务,在指定时间之后执行任务以及在某个时间之后每隔时间段重复执行的任务...

    python入门到高级全栈工程师培训 第3期 附课件代码

    05 生产者消费者模型 06 第三次作业讲解 第20章 01 上节课回顾 02 装饰器基本理论 03 高阶函数使用 04 函数闭包 05 函数闭包装饰器基本实现 06 函数闭包加上返回值 07 函数闭包加上参数 08 函数闭包补充:解压序列...

    Java开发技术大全 电子版

    8.4.4生产者-消费者问题实例284 8.5本章小结287 第9章运行时类型识别288 9.1RTTI的作用288 9.2用Class类来加载对象289 9.3使用getClass()方法获取类信息290 9.4使用类标记292 9.5使用关键字instanceof判断...

    JAVA面试题最全集

    12.synchronized (生产者和消费) 13.String 和 StringBuffer 14.Serializable 15.MVC (Struts的工作流程) 16.什么是MDA 17.tcp与udp的区别 18.链表与散列表和数组的区别 19.堆和栈的区别 20.ejb的分类及...

    Python核心编程第二版(ok)

     3.1.3 多个语句构成代码组()   3.1.4 代码组由不同的缩进分隔   3.1.5 同一行书写多个语句(;)   3.1.6 模块   3.2 变量赋值   3.2.1 赋值操作符   3.2.2 增量赋值   3.2.3 多重赋值  ...

    新版Android开发教程.rar

    ANDROID 的推出后可能影响的产业包括移动电信业,软件开发业,手机制造业,在以消费者为核心的状 态 。 对消费者的影响 � 高档手机选择面增加。 � A ndroid 在设计初期就考虑了与现其有业务的融合,改变以往从...

    JAVA程序设计教程

    第一章程序和程序设计 .......................................................................................................1 §1.1 什么是程序 ?........................................................

Global site tag (gtag.js) - Google Analytics