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

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

阅读更多

问题在《一个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 解决线程同步问题

    生产者消费者代码(C++版)

    在多线程编程中,"生产者-消费者"模型是一个经典的并发问题,它涉及到了如何在多个线程间共享资源并协同工作。本示例是使用C++实现的一个解决方案,结合了Posix信号量和互斥量来确保线程安全和同步。 生产者-消费者...

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

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

    生产者消费者问题源代码

    在计算机科学中,生产者消费者问题是一个经典的问题,它描述了多个生产者和消费者共享一个缓冲区的情形。在这个问题中,多个生产者将产品放入缓冲区中,而多个消费者则从缓冲区中取出产品。如果缓冲区为空,消费者将...

    解决线程死锁问题的 生产者 消费者程序.rar_linux_linux 线程_生产者_生产者 消费者_生产者消费者

    本资源“解决线程死锁问题的 生产者 消费者程序.rar”显然是针对这一问题提供的一种解决方案,它特别关注了Linux环境下的线程管理。"生产者-消费者"模型是一个经典的并发编程模型,用于演示如何有效地共享有限资源,...

    生产者与消费者实验代码

    生产者-消费者问题是一个经典的多线程同步问题,在计算机科学领域中,主要用于研究如何在多个线程之间进行协调工作的问题。该实验旨在通过编程实现生产者与消费者的逻辑,了解进程间的互斥访问机制以及临界区的概念...

    利用线程实现生产者消费者问题

    通过上述分析可以看出,该程序有效地解决了生产者消费者问题中的同步与互斥问题。使用信号量作为同步工具,不仅简化了代码逻辑,还提高了程序的并发性能。这种解决方案对于理解多线程编程及同步机制具有重要的意义。

    java 多线程 生产者消费者模式

    以下是一段简单的生产者消费者模式的代码示例: ```java import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class ProducerConsumerExample { public static...

    生产者消费者 Linux代码

    在操作系统设计中,生产者-消费者问题是一个经典的同步问题,它描述了两个或多个进程(生产者和消费者)如何共享一个有限的...通过分析和调试这段代码,我们可以更直观地感受生产者-消费者模式在解决同步问题上的应用。

    线程实现生产者消费者

    生产者消费者问题是多线程编程中的经典问题,描述了两个线程角色:生产者负责生成数据,消费者负责消耗数据。在共享数据结构(如缓冲区)中,生产者不能直接将数据推送给消费者,而消费者也不能在缓冲区为空时尝试...

    编程实现生产者消费者或读写者的同步问题

    本文将详细介绍如何通过编程方式解决生产者消费者问题,这是一个经典的多线程同步问题。在本例中,我们将采用C++语言结合Windows API来实现。该示例程序展示了如何在同一个进程地址空间内,让多个线程(生产者线程与...

    多线程实现生产者---消费者模型

    通过分析和理解这段代码,我们可以更好地掌握多线程编程和并发控制的概念,这对于理解和优化多线程应用程序至关重要。同时,这也是操作系统课程中的一个重要实践环节,帮助学生理论联系实际,提升解决实际问题的能力...

    MFC生产者消费者问题

    在计算机科学中,生产者消费者问题是多线程编程中的一个经典问题,主要涉及线程间的同步与通信。在这个问题中,生产者线程负责生成数据,而消费者线程则负责消费这些数据。为了保证数据的一致性和避免资源冲突,我们...

    多线程开发及其源代码

    2. 生产者-消费者问题:线程间共享数据时,生产者线程生产和消费者线程消费需协调。可以使用队列和信号量解决。 3. 线程安全:确保多线程访问共享资源时不会出错。使用原子操作、线程局部存储、不可变对象等方式增强...

    多线程代码实例

    在IT领域,多线程是一种常见的编程模型,用于提高程序的执行效率,特别是在并发和并行处理场景下。本文将详细解析"多线程代码实例",主要关注在Linux环境中,使用C语言实现的生产消费管理系统,以及如何利用POSIX...

    “生产者-消费者”问题模拟系统实验报告+代码

    在计算机科学领域,"生产者-消费者"问题是一个经典的多线程同步问题,它涉及到并发编程中的资源管理和数据共享。该问题描述了两个角色:生产者(Producer)和消费者(Consumer),他们共同操作一个有限容量的缓冲区...

    多线程_生产消费模型

    总结来说,Windows多线程环境下的生产者-消费者模型通过关键代码段和互斥量实现线程同步,有效地解决了并发访问共享资源的问题。关键代码段适用于同一进程内的线程同步,而互斥量则可跨越进程边界,保证全局资源的...

    实验二.多线程应用程序设计

    多线程应用程序设计 多线程应用程序设计是指在一个进程中运行多个线程,每个线程共享进程的资源,包括打开的文件、页表、信号标识及动态分配的内存...但是,多线程程序的编写也需要注意线程间通讯和数据共享的问题。

Global site tag (gtag.js) - Google Analytics