黑马程序员技术交流社区
标题:
关于多线程中监视器方法的问题
[打印本页]
作者:
haozi050
时间:
2014-2-1 21:05
标题:
关于多线程中监视器方法的问题
本帖最后由 haozi050 于 2014-2-9 08:35 编辑
在多生产者多消费者例子中,如果只创建一个监视器方法对象的话
(如代码一),结果是只生产了一个馒头还没有消费,而且死锁了。我猜想是这个监视器让所有线程处于等待状态了。如果创建两个监视器对象的话,问题就解决了(如代码二)。
这时调用
await()
方法为什么没死锁呢?
问题是监视器对象它怎么知道是一个监视器对应生产者,一个对应消费者呢?这中间的到底是怎么监视的?请高手回答详细点,谢了!
代码一:
import java.util.concurrent.locks.*;
class Resource
{
// 定义商品名称和编号
private String name;
private int count=1;
// 添加一个标记,如果为假则生产否则不生产只消费。
boolean flag=false;
// 为方便起见,将生产馒头方式和消费馒头方式定义在这里
Lock lock=new ReentrantLock();
// 根据锁获取监视器方法对象
Condition c=lock.newCondition();
public void set(String name)
{
try{
lock.lock();
while(flag)
{
try{c.await();}catch(InterruptedException e){}
// 这个程序的结果是只生产了一个馒头还没有消费,而且死锁了。可以推断出这个监视器让所有线程处于等待状态了。所以要想只让本方
// 等待的话,要创建两个监视器对象。
}
this.name=name+"--"+count;
System.out.println(Thread.currentThread().getName()+"...生产者..."+name+count);
count++;
flag=true;
c.signalAll();
}finally{
lock.unlock();
}
}
public synchronized void get()
{
// 对于消费者如果为假的话表示没有馒头需要等待,如果为真的话就消费,然后在将标记改为假,唤醒生产者,自己再等待。
try{
lock.lock();
while(!flag)
{
try{c.await();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...消费者..."+name);
flag=false;
c.signalAll();
}
}
finally{
lock.unlock();
}
}
}
class Producer1 implements Runnable
{
Resource r;
Producer1(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.set("馒头");
}
}
}
class Consumer1 implements Runnable
{
Resource r;
Consumer1(Resource r)
{
this.r=r;
}
public void run()
{
while(true)
{
r.get();
}
}
}
class ProductorConsumerDemo2
{
public static void main(String []args)
{
Resource r=new Resource();
Producer1 p=new Producer1(r);//创建生产任务
Consumer1 c=new Consumer1(r);//创建消费任务
Thread t0=new Thread(p);//创建线程明确生产任务
Thread t1=new Thread(p);//创建线程明确生产任务
Thread t2=new Thread(c);//创建线程明确消费任务
Thread t3=new Thread(c);//创建线程明确生产任务
t0.start();
t1.start();
t2.start();
t3.start();
}
}
复制代码
代码二:
import java.util.concurrent.locks.*;
//定义资源类
class OneResource
{
private String name;//名称
private int count=1;//编号
boolean flag=false;//标记
// 创建一个锁对象
Lock lock=new ReentrantLock();
// 创建两个监视器方法对象,一个监视生产者,一个监视消费者
Condition produce=lock.newCondition();
Condition consume=lock.newCondition();
public void set(String name)//定义生产馒头方法
{
// 上锁
lock.lock();
// 如果标记为假就进行生产否则就等待。由于会抛异常,防止异常之后没释放锁,要用try finally
try{
while(flag)
{try{produce.await();}catch(InterruptedException e){}}
this.name=name+"--"+count;
System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
count++;
// 生产完之后将标记改为真,唤醒消费者线程。
flag=true;
consume.signal();
}
// 释放锁
finally{
lock.unlock();
}
}
public void get()//定义消费方法
{
lock.lock();
try{
// 如果标记为真则消费,否则等待
while(!flag)
{
{try{consume.await();}catch(InterruptedException e){}}
}
System.out.println(Thread.currentThread().getName()+"..消费者.."+this.name);
// 消费完之后将标记改为假,唤醒生产者
flag=false;
produce.signal();
}finally{
lock.unlock();
}
}
}
//定义生产者
class MoreProducer implements Runnable
{
// 为了保证资源一致,这里定义构造函数接受资源对象,一初始化时就有资源对象。
OneResource r;
MoreProducer(OneResource r)
{
this.r=r;
}
// 明确生产者的任务,生产馒头
public void run()
{
// 一直生产
while(true)
r.set("馒头");
}
}
//定义消费者类
class MoreConsumer implements Runnable
{
// 一初始化也要有统一的资源对象
OneResource r;
MoreConsumer(OneResource r)
{
this.r=r;
}
// 明确消费者的消费任务
public void run()
{
// 一直消费
while(true)
r.get();
}
}
public class ProducerConsumerFinal {
public static void main(String[] args) {
//创建资源对象,并将资源传递给生产者消费者
// 创建任务对象即生产任务和消费任务,生产任务和消费任务都只有一个
OneResource r=new OneResource();
MoreProducer p1=new MoreProducer(r);
MoreConsumer c1=new MoreConsumer(r);
// 创建线程并指定线程任务
Thread t0=new Thread(p1);
Thread t1=new Thread(p1);
Thread t2=new Thread(c1);
Thread t3=new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
复制代码
作者:
e.c
时间:
2014-2-4 15:41
本帖最后由 e.c 于 2014-2-4 15:44 编辑
代码1是楼主马虎的原因~
消费者:get 里的while循环结束的地方错了
还有get不要加synchronized
作者:
e.c
时间:
2014-2-4 15:45
e.c 发表于 2014-2-4 15:41
代码1是楼主马虎的原因~
消费者:get 里的while循环结束的地方错了
还有get不要加synchronized ...
while(!flag)
{
try{c.await();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...消费者..."+name);
flag=false;
c.signalAll();
} //循环结束应该放在try{}catch(){} 后面
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2