Java多线程—Executor框架线程池使用原始方式实现生产者消费者模式

Executor框架线程池使用原始方式实现生产者消费者模式

在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。。举个例子,如果你的Java程序中有两个线程——即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓 冲区中有内容待消费(不为空)。相应的,消费者可以通知生产者可以开始生成更多的数据,因为当它消耗掉某些数据后缓冲区不再为满。

      wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视。本文对这些关键字的使用进行了描述。

       我们可以利用wait()来让一个线程在某些条件下暂停运行。例如,在生产者消费者模型中,生产者线程在缓冲区为满的时候,消费者在缓冲区为空的时 候,都应该暂停运行。如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信 号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。在这篇文章中你将会学到如何使用 wait、notify 和 notifyAll 来实现线程间的通信,从而解决生产者消费者问题。

wait, notify, notifyAll 范例

 MyProducer(生产者)和MyCustomer(消费者),他们分别继承了Runnable。Main线程开始了生产者和消费者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往 LinkedList里插入随机整数直到LinkedList满。我们在while(queue.isEmpty())循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在 我们检查条件时改变这个队列。如果队列满了,那么MyProducer线程会在MyCustomer线程消耗掉队列里的任意一个整数,并用notify来通知MyProducer线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。

public class MyProducer implements Runnable{
 
	private LinkedList<Integer> queue;
	
	public MyProducer(LinkedList<Integer> queue) {
		super();
		this.queue = queue;
	}
 
	@Override
	public void run() {
		for(int i =0 ;i<10;i++){
			synchronized (queue) {
				while(!queue.isEmpty()){
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("producer..."+i);
				queue.add(i);
				queue.notifyAll();
			}
		}
	}
}
public class MyCustomer implements Runnable{
 
	private LinkedList<Integer> queue;
	
	public MyCustomer(LinkedList<Integer> queue) {
		super();
		this.queue = queue;
	}
 
	@Override
	public void run() {
		for(int i =0 ;i<10;i++){
			synchronized (queue) {
				while(queue.isEmpty()){
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("customer..."+i);
				queue.remove();
				queue.notifyAll();
			}
		}
	}
 
}

测试代码:


public class Client {
	
	public static void main(String[] args) {
		
		LinkedList<Integer> queue = new LinkedList<Integer>();
		
		MyProducer producer = new MyProducer(queue);
		
		MyCustomer customer = new MyCustomer(queue);
		
		ExecutorService executor = Executors.newFixedThreadPool(4);
		
		executor.submit(customer);
		
		executor.submit(producer);
		
		executor.shutdown();
		
	}
 
}

测试结果:

producer...0
customer...0
producer...1
customer...1
producer...2
customer...2
producer...3
customer...3
producer...4
customer...4
producer...5
customer...5
producer...6
customer...6
producer...7
customer...7
producer...8
customer...8
producer...9
customer...9

结论:

1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。

2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

5. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。

这是关于Java里如何使用wait, notify和notifyAll的所有重点啦。你应该只在你知道自己要做什么的情况下使用这些函数,不然Java里还有很多其它的用来解决同步问题的方 案。例如,如果你想使用生产者消费者模型的话,你也可以使用BlockingQueue,它会帮你处理所有的线程安全问题和流程控制。如果你想要某一个线 程等待另一个线程做出反馈再继续运行,你也可以使用CycliBarrier或者CountDownLatch。如果你只是想保护某一个资源的话,你也可 以使用Semaphore。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值