生产者-消费者模型的相关问题

来源:9-1 synchronized

慕斯6088333

2019-04-09

翔哥,您好,我刚听完第八章 和第九章的第一节。尝试着,写了一下producer-and-consumer 代码。

有几个问题,网上找了一些资料。有几个点还不是特别理解,求指导。

先上代码

package com.imooc.thread;

import java.util.LinkedList;
import java.util.List;


/**
 * Single producer and consumer model 
 * @author tan3
 *
 */
public class ProducerAndConsumer {
	
	private static final List<Integer> BUFFER = new LinkedList<>();
	private static final Integer BUFFER_MAX_SIZE = 3;
	private static Integer productNum = 0;
	private static final Integer ITERATION = 10;
	
	public static void main(String[] args) {
		final Object lock = new Object();
		Thread producerThread = new Thread(new Runnable() {		
			@Override
			public void run() {
				// if the buffer is full, release the lock 
				synchronized(lock) {
					for(int idx =0; idx < ITERATION ;idx++) {
						if(BUFFER.size() == BUFFER_MAX_SIZE) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						// if the buffer is not full, produce one good and wake up the consumer
						try {
							System.out.println(Thread.currentThread().getName()
									   + " produce product: "+ productNum);
							Thread.sleep(1000);
							BUFFER.add(productNum++);
							lock.notifyAll();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				
			}
			
		},"Producer");
		
		Thread consumerThread = new Thread(new Runnable() {
			@Override
			public void run() {
				for(int idx =0; idx < ITERATION ;idx++) {
				// if the buffer is empty, release the lock and wake up the producer 
					synchronized(lock) {
						if(BUFFER.size() == 0) {
							try {
								lock.wait();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						// if the buffer is not empty, produce one good and tell the consumer to produce one
						try {
							System.out.println(Thread.currentThread().getName()
									   + " consume product: "+ BUFFER.remove(0));
							Thread.sleep(1000);
							lock.notifyAll();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
					}
				}
			}
			
		},"Consumer");
		
		producerThread.start();
		consumerThread.start();
		
		/*try {
			producerThread.join();
			consumerThread.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
	}

}

几个疑惑的点。

  1. 我这里用的是if(BUFFER.size() == BUFFER_MAX_SIZE) if语句进行判断的。网上说应该是用while 语句。 【在多线程中要测试某个条件的变化,使用if 还是while】 , 我测试了一下,我的程序跑了几遍。好像没出什么问题。(但是好像网上搜到的更多的是使用wait方法)。想知道这个地方到底该怎么理解。

  2. https://www.geeksforgeeks.org/producer-consumer-solution-using-threads-java/
    第二问题是关于join方法的。这个答案,最后用了join方法来确保生产线程 finishes before 消费者线程。(我试了一下,好像没什么用。)似乎这里不涉及抢占资源的问题吖?)

  3. 如果扩展multiple consumer and multiple producer,是不是BUFFER改成violate。以及voilate 是不是不可以跟final 并用。这里是用final 还是voilate。 怎么保证线程安全。我觉得final 就是保证线程安全比较好的方式了。。

可能概念有点多。学得越多越糊涂了。希望和大家探讨。

写回答

2回答

翔仔

2019-04-10

同学好,我一个个问题回答哈

  1. 现在只有一个生产者,使用if是没问题的,如果存在多个生产者的话,是会多生产的,此时需要使用while,因为调用wait之后被唤醒是会接着执行的。

  2. 代码里其实已经决定哪个线程先执行完了,所以不需要join方法。

  3. 使用final是不能保证安全的,多个生产者或消费者的话,每次也只有一个能获取到对象锁,直接用现在这种写法就可以,无非就是多起几个线程。


1
3
慕斯6088333
回复
翔仔
好的。了解了。
2019-04-10
共3条回复

慕斯6088333

提问者

2019-04-10

我发现翔哥,你公开课讲的deadlock demo 。我尝试着加了语句,解开dead lock 。我以为先后顺序是用join方法控制,确保线程1把锁释放,让线程2拿到锁,然后让线程2执行完,结束生命周期前唤醒线程1,然后线程1自然而然有了b的锁。。到底是线程1还是2先执行完取决于谁调用wait方法和执行前后顺序有关。那么join到底啥时候用啊

package com.imooc.thread;

public class DeadLockDemo {
	
	public static void main(String[] args) {
		final Object lockA = new Object();
		final Object lockB = new Object();
		
		Thread t1 = new Thread( new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				String name = Thread.currentThread().getName();
				synchronized(lockA) {
					System.out.println(name +" got lock A, waiting to get lock B");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					try {
						lockA.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized(lockB) {
						System.out.println(name +" got lock B");
						System.out.println(name+"finish");
					}
				}
				
			}
			
		},"Thread A");
		
		Thread t2 = new Thread( new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				String name = Thread.currentThread().getName();
				synchronized(lockB) {
					System.out.println(name +" got lock B, waiting to get lock A");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized(lockA) {
						System.out.println(name +" got lock A");
						System.out.println(name+"finish");
						lockA.notifyAll();
					}
				}
				
			}
			
		},"Thread B");
		t1.start();
		/*try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		t2.start();	
		
		
	}
}


0
2
慕斯6088333
回复
翔仔
卧槽。这么一说那还真的是这样。。会更容易理解一些。。
2019-04-10
共2条回复

剑指Java面试-Offer直通车 百度资深面试官授课

招聘季即将到来,让百度资深面试官来为你的高薪Offer保驾护航

8427 学习 · 1870 问题

查看课程