问题_多线程_死锁_2个生产者1个消费者

秋···· 2018-12-16 11:12:22
2个生产者使用同一个生产器生产资源,1个消费者消费该生产器生产的资源(一次只能生产/消费一个)
为什么两个生产者互相唤醒,而不唤醒被冻结的消费者?
请无视2楼和3楼(找不到删除的地方=。=)
该问题可以通过将while中的notify()改为notifyAll()解决,可为什么notify()不行?该方法不是随机唤醒监视者线程池中的一个线程麽?
代码见楼下。
...全文
387 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_39936465 2018-12-19
  • 打赏
  • 举报
回复
你可以试试1个生产者 2个消费者,这个时候 会死锁在消费者,如果是2个生产者2个消费者,你的程序可以正常运行。因为你的程序有生产者和消费者2不同个进程,这里还是要说 notify只能唤醒一个进程,jvm有自己判断进程优先的机制,一旦jvm判断哪个进程优先的话notify事实上只能唤醒优先的那个进程。
qq_39936465 2018-12-18
  • 打赏
  • 举报
回复
进程唤醒是随机不可预测的,你不会以为随机就是平分几率吧!优先唤醒解释权在jvm,所以你的程序在不同平台会有不同的结果,你的情况就是jvm 认为,生产者的进程优先权高于了消费者,然后就进入了死锁状态。编程要排除这种环境造成的情况你认为呢,这里纠结notify不起作用没有意义。
秋···· 2018-12-18
  • 打赏
  • 举报
回复
引用 8 楼 qq_39936465 的回复:
[quote=引用 6 楼 m0_37851980 的回复:] [quote=引用 4 楼 qq_39936465 的回复:] notify()只能唤醒一个进程,当然会优先唤醒同一个同步块的进程。
修改后的代码没有死锁是因为把两个notify()方法改成了notifyAll(),不是因为减少了两个wait()和notify()[/quote] 都说了notify只能唤醒一个进程,你数数你有几个进程?[/quote] 不算主线程和其它线程,一共有3个,两个生产者线程,一个消费者线程。我的代码里三个线程每次陷入wait()前都先使用notify()唤醒一个线程。而我的问题是运行一段时间后生产者的notify()只唤醒了另一个生产者。如果原因是你上述的优先唤醒同一个代码块的进程,我觉得还有点可能,不要一直纠结我会不懂notify()只能唤醒一个进程吧?
qq_39936465 2018-12-18
  • 打赏
  • 举报
回复
引用 6 楼 m0_37851980 的回复:
[quote=引用 4 楼 qq_39936465 的回复:]
notify()只能唤醒一个进程,当然会优先唤醒同一个同步块的进程。

修改后的代码没有死锁是因为把两个notify()方法改成了notifyAll(),不是因为减少了两个wait()和notify()[/quote]

都说了notify只能唤醒一个进程,你数数你有几个进程?
qq_39936465 2018-12-18
  • 打赏
  • 举报
回复
你插入这么多wait()和notify()不是拖累程序运行么。本来就是以抢占方式运行的,频繁设置wait()没意义。
qq_39936465 2018-12-17
  • 打赏
  • 举报
回复
进程写的wait太多了,修改一下


class Resource {
private String name;
private int count = 0;
private boolean flag = false; // false表示资源中没有一个资源,true表示有一个资源

// |资源对象生产一个资源|
public synchronized void set(String name) {
System.out.println(Thread.currentThread().getName() + "正在生产");
// 功能1
while (flag) // 不断{检测Resource中是否有资源,有资源时唤醒一个线程,等待}
try {
System.out.println(Thread.currentThread().getName() + "发现有一只烤鸭,通知下一个线程,进入冻结");
wait();
} catch (InterruptedException e) {
}

this.name = name + ++count;
System.out.println(Thread.currentThread().getName() + "...生产..." + this.name);
flag = true;
System.out.println("生产完烤鸭,叫醒一个进程," + Thread.currentThread().getName() + "进入冻结");
System.out.println(Thread.currentThread().getName() + "已冻结");
notifyAll();
}

// |消费对象消费一个资源|
public synchronized void out() {
System.out.println(Thread.currentThread().getName() + "正在消费");
while (!flag) // 不断{不断{检测Resource中是否有资源,无资源时唤醒一个线程,等待}
try {
System.out.println(Thread.currentThread().getName() + "发现没有烤鸭,通知下一个线程,进入冻结");
wait();
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "...消费..." + this.name);
flag = false;
System.out.println("吃完烤鸭了,叫醒一个进程," + Thread.currentThread().getName() + "进入冻结");
System.out.println(Thread.currentThread().getName() + "已冻结");
notifyAll();
}
}

// 生产者类
class Producer implements Runnable {
private Resource r;

Producer(Resource r) {
this.r = r;
}

public void run() {
// 一个生产者对象不断{使用r生产烤鸭}
while (true) {
r.set("烤鸭");
}
}

}

// 消费者类
class Consumer implements Runnable {
private Resource r;

Consumer(Resource r) {
this.r = r;
}

public void run() {
// 一个消费者对象不断{使用r消费烤鸭}
while (true) {
r.out();
}
}

}

class Demo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro1 = new Producer(r);
Producer pro2 = new Producer(r);
Consumer con = new Consumer(r);

Thread p1 = new Thread(pro1, "生产者1号");
Thread p2 = new Thread(pro2, "生产者2号");

Thread c1 = new Thread(con, "消费者1号");

p1.start();
p2.start();

c1.start();
}
}


运行结果

吃完烤鸭了,叫醒一个进程,消费者1号进入冻结
消费者1号已冻结
消费者1号正在消费
消费者1号发现没有烤鸭,通知下一个线程,进入冻结
生产者2号...生产...烤鸭73747
生产完烤鸭,叫醒一个进程,生产者2号进入冻结
生产者2号已冻结
生产者2号正在生产
生产者2号发现有一只烤鸭,通知下一个线程,进入冻结
生产者1号发现有一只烤鸭,通知下一个线程,进入冻结
消费者1号...消费...烤鸭73747
吃完烤鸭了,叫醒一个进程,消费者1号进入冻结
消费者1号已冻结
消费者1号正在消费
消费者1号发现没有烤鸭,通知下一个线程,进入冻结
生产者1号...生产...烤鸭73748
生产完烤鸭,叫醒一个进程,生产者1号进入冻结
生产者1号已冻结
生产者1号正在生产
生产者1号发现有一只烤鸭,通知下一个线程,进入冻结
生产者2号发现有一只烤鸭,通知下一个线程,进入冻结
消费者1号...消费...烤鸭73748
吃完烤鸭了,叫醒一个进程,消费者1号进入冻结
消费者1号已冻结
消费者1号正在消费
消费者1号发现没有烤鸭,通知下一个线程,进入冻结
生产者2号...生产...烤鸭73749
生产完烤鸭,叫醒一个进程,生产者2号进入冻结
qq_39936465 2018-12-17
  • 打赏
  • 举报
回复
notify()只能唤醒一个进程,当然会优先唤醒同一个同步块的进程。
秋···· 2018-12-17
  • 打赏
  • 举报
回复
引用 4 楼 qq_39936465 的回复:
notify()只能唤醒一个进程,当然会优先唤醒同一个同步块的进程。
修改后的代码没有死锁是因为把两个notify()方法改成了notifyAll(),不是因为减少了两个wait()和notify()
秋···· 2018-12-16
  • 打赏
  • 举报
回复

/*
	描述:
		有3个类:资源类,生产者类,消费者类
		资源类可以生产生产者类指定名称的资源,消费者类可以消费资源类的资源

	目的:
		2个生产者使用同一个生产器生产资源,1个消费者消费该生产器生产的资源(一次只能生产/消费一个)

	步骤:
		资源对象r;
		功能1:|不断{不断{检测Resource中是否有资源,有资源时唤醒一个线程,等待},无资源时{生产资源,唤醒一个线程,进入冻结}}|
		功能2:|不断{不断{检测Resource中是否有资源,无资源时唤醒一个线程,等待},有资源时{消费资源,唤醒一个线程,进入冻结}}|

		2个生产者对象pro不断{使用r.功能1}
		1个消费者对象con不断{使用r.功能2}

	问题:
		为什么两个生产者互相唤醒,而不唤醒被冻结的消费者?

	

*/

//资源类
class Resource
{
	private String name;
	private int count = 0;
	private boolean flag = false;	//false表示资源中没有一个资源,true表示有一个资源

	//|资源对象生产一个资源|
	public synchronized void set(String name)
	{
		System.out.println(Thread.currentThread().getName() + "正在生产");
		//功能1
		while(flag)		//不断{检测Resource中是否有资源,有资源时唤醒一个线程,等待}
		try
		{
			System.out.println(Thread.currentThread().getName() + "发现有一只烤鸭,通知下一个线程,进入冻结");
			notify();

			this.wait();
		}
		catch(InterruptedException e){}

		this.name = name + ++count;
		System.out.println(Thread.currentThread().getName() + "...生产..." + this.name);
		flag = true;
		//notify();
		try
		{
			System.out.println("生产完烤鸭,叫醒一个进程," + Thread.currentThread().getName() + "进入冻结");
			notify();
			System.out.println(Thread.currentThread().getName() + "已冻结");
			this.wait();
			
		}
		catch(InterruptedException e){}
	}

	//|消费对象消费一个资源|
	public synchronized void out()
	{
		System.out.println(Thread.currentThread().getName() + "正在消费");
		while(!flag)	//不断{不断{检测Resource中是否有资源,无资源时唤醒一个线程,等待}
			try
			{
				System.out.println(Thread.currentThread().getName() + "发现没有烤鸭,通知下一个线程,进入冻结");
				notify();
				this.wait();
			}
			catch(InterruptedException e){}
		System.out.println(Thread.currentThread().getName() + "...消费..." + this.name);
		flag = false;
		try
		{
			System.out.println("吃完烤鸭了,叫醒一个进程," + Thread.currentThread().getName() + "进入冻结");
			notify();
			System.out.println(Thread.currentThread().getName() + "已冻结");
			this.wait();
			
		}
		catch(InterruptedException e){}
		//notify();
		
	}
}

//生产者类
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}

	public void run()
	{
		//一个生产者对象不断{使用r生产烤鸭}
		while(true)
		{
			r.set("烤鸭");
		}
	}

}

//消费者类
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}

	public void run()
	{
		//一个消费者对象不断{使用r消费烤鸭}
		while(true)
		{
			r.out();
		}
	}

}


class Demo
{
	public static void main(String[] args)
	{
		Resource r = new Resource();
		Producer pro1 = new Producer(r);
		Producer pro2 = new Producer(r);
		Consumer con = new Consumer(r);

		Thread p1 = new Thread(pro1, "生产者1号");
		Thread p2 = new Thread(pro2, "生产者2号");


		Thread c1 = new Thread(con, "消费者1号");

		p1.start();
		p2.start();

		c1.start();
	}
}
秋···· 2018-12-16
  • 打赏
  • 举报
回复
在这里贴代码缩进好像都不见了?。。。sry=。=
秋···· 2018-12-16
  • 打赏
  • 举报
回复
上面是可运行代码,运行后观察到消费者进入冻结状态,生产者1和2互相唤醒,却不唤醒消费者,演变成死锁。为什么不唤醒消费者?

62,624

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧