黑马程序员技术交流社区

标题: 关于JDK1.5的生产者和消费者。。。急求大婶解决。。。。... [打印本页]

作者: 赵太云    时间: 2013-2-18 20:08
标题: 关于JDK1.5的生产者和消费者。。。急求大婶解决。。。。...
本帖最后由 赵太云 于 2013-3-6 14:18 编辑

复制代码
不是说Lock对象的Condition可以只唤醒指定Condition对象上等待的线程,那为什么在等待时,还要用while。具体看以下代码中红色注释。。。

package reviews;

import java.util.concurrent.locks.*;

public class Review{
        public static void main(String[] args){
                Resource r = new Resource();
                new Thread(new Producer(r)).start();
                new Thread(new Producer(r)).start();
               
                new Thread(new Consumer(r)).start();
                new Thread(new Consumer(r)).start();
        }
}

class Resource{
        private String name;
        private int count = 1;
        private boolean flag = false;
        
        private Lock lock = new ReentrantLock();
        private Condition con_pro = lock.newCondition();
        private Condition con_con = lock.newCondition();
        
        public void set(String name){
                lock.lock();
                try {
                        if (flag){//这里的if为什么要改用while? 因为只可以只唤醒对方线程。。为什么还要用while??????????
                                con_pro.await();
                        }
                        this.name = name + count++;
                        System.out.println(Thread.currentThread().getName() + "  producer          " + this.name);
                        this.flag = true;
                        con_con.signal();
                }catch (InterruptedException i){
                        i.printStackTrace();
                }finally {
                        lock.unlock();
                }
        }
        
        public void print(){
                lock.lock();
                try {
                        if (!flag){
                                con_con.await();
                        }
                        System.out.println(Thread.currentThread().getName() + "  consumer  " + this.name);
                        this.flag = false;
                        con_pro.signal();
                }catch (InterruptedException i){
                        i.printStackTrace();
                }finally {
                        lock.unlock();
                }
        }
}

class Producer implements Runnable{
        private Resource r;
        public Producer(Resource r){
                this.r = r;
        }
        
        public void run(){
                while (true){
                        r.set("商品");
                }
        }
}

class Consumer implements Runnable{
        private Resource r;
        public Consumer(Resource r){
                this.r = r;
        }
        
        public void run(){
                while (true){
                        r.print();
                }
        }
}


作者: 朱玉玺    时间: 2013-2-18 20:27
建议:再看一遍老毕的视频。
这里要用while是因为你启动了多个生产者线程,如果不用while再次判断下,很可能出现线程全部冻结的情况。如果只有一个生产者,当然就用不着while 了
作者: 陈丽莉    时间: 2013-2-18 20:53
就是楼上说的那样了,我画蛇添足再解释解释,虽然唤醒的只是对方线程,但是对方线程也是有多个的,所以为了不发生混乱,要用while循环判断。
作者: 赵太云    时间: 2013-2-18 21:02
朱玉玺 发表于 2013-2-18 20:27
建议:再看一遍老毕的视频。
这里要用while是因为你启动了多个生产者线程,如果不用while再次判断下,很可 ...

不是啊。。
是不等啊。。
用while的目的是为了防止唤醒本方线程。。。。。。。。。。
但这里又不会唤醒本方线程。。。。。。。。。。。。。。
作者: 赵太云    时间: 2013-2-18 21:12
陈丽莉 发表于 2013-2-18 20:53
就是楼上说的那样了,我画蛇添足再解释解释,虽然唤醒的只是对方线程,但是对方线程也是有多个的,所以为了 ...

对方线程是有多个,但是这里只用signal()方法,只会唤醒一个啊。。。。。

还是不懂。。。。。。。。。

视频我看了好几次了..


作者: 朱玉玺    时间: 2013-2-18 21:36
赵太云 发表于 2013-2-18 21:12
对方线程是有多个,但是这里只用signal()方法,只会唤醒一个啊。。。。。

还是不懂。。。。。。。。。

“阻塞的线程都放在线程池中。notify唤醒的都是线程池中的线程,通常是线程池中的第一个线程。notifyAll唤醒线程池中的所有阻塞线程。”你的signal可能唤醒的就是自己这边的线程。
作者: 赵太云    时间: 2013-2-18 22:00
朱玉玺 发表于 2013-2-18 21:36
“阻塞的线程都放在线程池中。notify唤醒的都是线程池中的线程,通常是线程池中的第一个线程。notifyAll ...

不对啊。。。你没有好好看我的代码。我唤醒的是消费者线程: con_con.signal();

并没有唤醒本方线程啊。。老毕也这么说的。。。。。
作者: 朱玉玺    时间: 2013-2-18 22:10
赵太云 发表于 2013-2-18 22:00
不对啊。。。你没有好好看我的代码。我唤醒的是消费者线程: con_con.signal();

并没有唤醒本方线程啊 ...

恩,发现了。你试试有bug没?其实,不管怎样,用while多一层判断,总是安全点~
作者: 赵太云    时间: 2013-2-18 23:01
朱玉玺 发表于 2013-2-18 22:10
恩,发现了。你试试有bug没?其实,不管怎样,用while多一层判断,总是安全点~ ...

恩。。
我测试了多次,但是只有用while时才可以一对一输出!!!我就为这个不解。。。。。。。。。
作者: 朱玉玺    时间: 2013-2-19 00:48
本帖最后由 朱玉玺 于 2013-2-19 01:00 编辑

按照图上的执行路径走一遍就明白了。那个执行顺序,是根据程序的一次运行结果推出来的。牢记一点:唤醒对方,不等于一定让对方执行,只是让其从冻结状态恢复到有资格获取执行权。

QQ截图201302190045500000.jpg (90.04 KB, 下载次数: 45)

QQ截图201302190045500000.jpg

作者: 罗海云    时间: 2013-2-19 09:33
因为不用while的话, 线程醒来就直接从睡那儿直接往下走, 用while就避免了这种问题.醒来过后还要判断下条件, 成立才走, 不成立就完了.
作者: 陈丽莉    时间: 2013-2-19 11:25
本帖最后由 陈丽莉 于 2013-2-19 11:30 编辑

问下,这个代码是视频中写的吧,是哪天的,我去仔细瞅瞅再看看能不能解释清楚~
PS:看看楼上说的~ 就是那么回事儿 生产者和消费者都创建了多线程,但con_pro和con_con只区分了生产者和消费者,并没有区分同一类型的不同线程
作者: 占琳    时间: 2013-2-22 21:40
while的特性啊  括号里的条件为真符合才进行运算啊
作者: 杨玉辉    时间: 2013-3-4 03:18
赵太云 发表于 2013-2-18 21:12
对方线程是有多个,但是这里只用signal()方法,只会唤醒一个啊。。。。。

还是不懂。。。。。。。。。

老兄 请想一想 主函数是不是new Thread(new Producer(r)).start();
                                         new Thread(new Producer(r)).start();
                 
                new Thread(new Consumer(r)).start();
                new Thread(new Consumer(r)).start();
就问你 是不是Producer 、 Consumer 都创建了两个 线程 ?

con_con.signal();  就是唤醒Consumer 消费线程吧  ,问你唤醒了是不是两个线程? 或者说,这俩线程是不是共享同一个锁

要是用if语句  是不是只判断了一次啊? 俩个线程 就可以依次进行啊  问题就在这儿了吧 ,你生产了一个怎么能消费两次呢。  反过来就可以想通了吧,你就可以依次生产了两个,对吧。
注意是依次  每次只执行一个线程。 但是执行后,就产生了俩结果。

下面来 说说while 语句,            while(flag)
                                condition_pro.await();  //这是个循环结构吧
当你使用con_pro.signal();唤醒Producer的线程 ,是不是,俩个,之前你就创建的两个线程吗

用while 是否需要 每次都判定条件啊,必须的要判断吧。  问题就出现在这里,当你执行完第1个线程, 这个条件 flag的值 就被第一个线程改变了,你说是不是?
这第二个线程肯定就不可以执行,而是进入wait 状态吧, 这时候,你继续执行con_con.signal();,就该唤醒 Consumer的线程吧,  还是两个线程。 又重复上面的过程,通过while判断 让那一个等待的线程执行,之后,让新开始的进程 再进入wait 状态吧。 如此循环判断。

慢慢看视频,前后连起来看。不要着急,理清内在联系,相信着对哥们而言,不过尔尔!:victory:
作者: 宋朋山    时间: 2013-3-4 10:41
await();状态被signal后,代码是继续往下走的,而不是从run方法的第一句开始。所以如果是if的话,就不会再进行条件判定了。而用while的话是每次都判定的。你想想有没有可能,flag为true,但是被跳过的情况。
作者: 赵太云    时间: 2013-3-6 14:14
陈丽莉 发表于 2013-2-19 11:25
问下,这个代码是视频中写的吧,是哪天的,我去仔细瞅瞅再看看能不能解释清楚~
PS:看看楼上说的~ 就是那么 ...

是多线程那里的。。。。。。。。。。。。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2