Java并发编程:阻塞队列  

 

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

 

Java 1.5之后,在java.util.concurrent包下提供了若干个阻塞队列

       ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

       LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。

       PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

       DelayQueue:一个使用优先级队列实现的无界阻塞队列。

       SynchronousQueue:一个不存储元素的阻塞队列。

       LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

       LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

 

简单的例子:创建两个线程用来存放数据,一个线程用来取数据,用阻塞队列BlockingQueue实现。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueTest {
	public static void main(String[] args) {
		final BlockingQueue queue = new ArrayBlockingQueue(3);

		for (int i = 0; i < 2; i++) {
			new Thread() {
				public void run() {
					while (true) {
						try {
							Thread.sleep((long) (Math.random() * 1000));
							System.out.println(Thread.currentThread().getName()
									+ "准备放数据!");
							queue.put(1);
							System.out.println(Thread.currentThread().getName()
									+ "已经放了数据," + "队列目前有" + queue.size()
									+ "个数据");
						} catch (InterruptedException e) {
							e.printStackTrace();
						}

					}
				}

			}.start();
		}

		new Thread() {
			public void run() {
				while (true) {
					try {
						// 将此处的睡眠时间分别改为100和1000,观察运行结果
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getName()
								+ "准备取数据!");
						queue.take();
						System.out.println(Thread.currentThread().getName()
								+ "已经取走数据," + "队列目前有" + queue.size() + "个数据");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		}.start();
	}
}

 用阻塞队列改写上一个程序程序:

子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次。

import java.util.Collections;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueCommunication {

	public static void main(String[] args) {

		final Bussiness bussiness = new Bussiness();

		new Thread(new Runnable() {
			public void run() {
				for (int i = 1; i <= 50; i++) {
					bussiness.sub(i);
				}
			}
		}).start();

		new Thread(new Runnable() {
			public void run() {
				for (int i = 1; i <= 500; i++) {
					bussiness.main(i);
				}
			}
		}).start();
	}

	static class Bussiness {
		BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1); // 管理子线程
		BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1); // 管理主线程
		{
			try {
				queue2.put(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void sub(int i) {
			try {
				queue1.put(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for (int j = 1; j <= 10; j++) {
				System.out.println("sub thread sequece of " + j + ",loop of "
						+ i);
			}

			try {
				queue2.take();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void main(int i) {
			try {
				queue2.put(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			for (int j = 1; j <= 100; j++) {
				System.out.println("main thread sequece of " + j + ",loop of "
						+ i);
			}
			try {
				queue1.take();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。