使用LinkedBlockingQueue来实现生产者消费者的例子

工作中,经常有将文件中的数据导入数据库的表中,或者将数据库表中的记录保存到文件中。为了提高程序的处理速度,可以设置读线程和写线程,这些线程通过消息队列进行数据交互。本例就是使用了LinkedBlockingQueue来模仿生产者线程和消费者线程进行数据生产和消费。
为了方便,这些不同的类被写在了一个类中,实际使用的时候,可以单独拆开,举一反三地使用。

以下是例子:
LinkedBlockingQueueDemo.java

import java.util.Date;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

public class LinkedBlockingQueueDemo {
	// 生产者线程数量
	private final static int providerThreadAmount = 5;

	// 记录每一个生产者线程是否处理完毕的标记
	private static boolean[] providerDoneFlag = new boolean[providerThreadAmount];

	// 整个所有的生产者线程全部结束的标记
	private static boolean done = false;

	// 一个线程安全的队列,用于生产者和消费者异步地信息交互
	private static LinkedBlockingQueue<String> linkedBlockingQeque = new LinkedBlockingQueue<String>();

	static class ProviderThread extends Thread {
		private Thread thread;
		private String threadName;
		private int threadNo;

		public ProviderThread(String threadName2, int threadNo) {
			this.threadName = threadName2;
			this.threadNo = threadNo;
		}

		public void start() {
			if (thread == null) {
				thread = new Thread(this, threadName);
			}

			thread.start();
			System.out.println(
					(new Date().getTime()) + " " + threadName + " starting... " + Thread.currentThread().getName());
		}

		@Override
		public void run() {
			int rows = 0;
			for (int i = 0; i < 100; i++) {
				String string = String.format("%s-%d-%s", threadName, i, Thread.currentThread().getName());
                // offer不会去阻塞线程,put会
				//linkedBlockingQeque.offer(string);
                linkedBlockingQeque.put(string);
				rows++;
				/*
				 * try { Thread.sleep((new Random()).nextInt(5) * 1000); } catch
				 * (InterruptedException e) { e.printStackTrace(); }
				 */
			}

			// 本线程处理完毕的标记
			LinkedBlockingQueueDemo.providerDoneFlag[threadNo] = true;
			System.out.println((new Date().getTime()) + " " + threadName + " end. total rows is " + rows + "\t"
					+ Thread.currentThread().getName());
		}
	}

	static class ConsumerThread implements Runnable {
		private Thread thread;
		private String threadName;

		public ConsumerThread(String threadName2) {
			this.threadName = threadName2;
		}

		public void start() {
			if (thread == null) {
				thread = new Thread(this, threadName);
			}

			thread.start();
			System.out.println(
					(new Date().getTime()) + " " + threadName + " starting... " + Thread.currentThread().getName());
		}

		@Override
		public void run() {
			int rows = 0;
			// 生产者线程没有结束,或者消息队列中有元素的时候,去队列中取数据
			while (LinkedBlockingQueueDemo.getDone() == false || linkedBlockingQeque.isEmpty() == false) {
				try {
                    //在甘肃电信的实际应用中发现,当数据的处理量达到千万级的时候,带参数的poll会将主机的几百个G的内存耗尽,jvm会提示申请内存失败,并将进程退出。网上说,这是这个方法的一个bug。
					//String string = linkedBlockingQeque.poll(3, TimeUnit.SECONDS);
                    String string = linkedBlockingQeque.poll();
					if (string == null) {
						continue;
					}

					rows++;

					System.out
							.println((new Date().getTime()) + " " + threadName + " get msg from linkedBlockingQeque is "
									+ string + "\t" + Thread.currentThread().getName());
					/*
					 * try { Thread.sleep((new Random()).nextInt(5) * 1000); } catch
					 * (InterruptedException e) { e.printStackTrace(); }
					 */

				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
			System.out.println((new Date().getTime()) + " " + threadName + " end total rows is " + rows + "\t"
					+ Thread.currentThread().getName());
		}
	}

	public static synchronized void setDone(boolean flag) {
		LinkedBlockingQueueDemo.done = flag;
	}
	
	public static synchronized boolean getDone() {
		return LinkedBlockingQueueDemo.done;
	}

	public static void main(String[] args) {
		System.out.println((new Date().getTime()) + " " + "process begin at " + Thread.currentThread().getName());
		System.out.println(
				(new Date().getTime()) + " " + "linkedBlockingDeque.hashCode() is " + linkedBlockingQeque.hashCode());

		// 启动若干生产者线程
		for (int i = 0; i < providerThreadAmount; i++) {
			String threadName = String.format("%s-%d", "ProviderThread", i);
			ProviderThread providerThread = new ProviderThread(threadName, i);
			providerThread.start();
		}

		// 启动若干个消费者线程
		for (int i = 0; i < 10; i++) {
			String threadName = String.format("%s-%d", "ConsumerThread", i);
			ConsumerThread consumerThread = new ConsumerThread(threadName);
			consumerThread.start();
		}

		// 循环检测生产者线程是否处理完毕
		do {
			for (boolean b : providerDoneFlag) {
				if (b == false) {
					/*
					 * try { Thread.sleep(3 * 1000); System.out.println((new Date().getTime()) +
					 * " "+"sleep 3 seconds. linkedBlockingQeque.size() is "+linkedBlockingQeque.
					 * size() + "\t" + Thread.currentThread().getName()); } catch
					 * (InterruptedException e) { e.printStackTrace(); }
					 */

					// 只要有一个生产者线程没有结束,则整个生产者线程检测认为没有结束
					break;
				}

				LinkedBlockingQueueDemo.setDone(true);
			}

			// 生产者线程全部结束的时候,跳出检测
			if (LinkedBlockingQueueDemo.getDone() == true) {
				break;
			}
		} while (true);

		System.out.println((new Date().getTime()) + " process done successfully\t" + Thread.currentThread().getName());
	}
}

结果略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值