最近找实习,发现各大公司对Java的多线程爱的很深沉,遂决定开扒java.util.concurrent包,防止自己忘了,也给初学者一个参考。以下内容都来自官方的API,例子是我自己造的。


今天的主角是ArrayBlockingQueue。


一 位置

java 数组 线程安全 java多线程操作数组_多线程

知道它实现了抽象类BlockingQueue即可,和它一样的小伙伴很多


二 定义

java 数组 线程安全 java多线程操作数组_多线程_02

我只截取了第一段,以后都不截图了,需要的自己下载一个j2se7.chm就行了。

翻译过来就是:用数组实现的、长度有限的BlockingQueue,元素先进先出,队头的元素停留在队列里的时间最长,而队尾的最短,队尾插入新元素,队头进行取元素操作。

试图插元素到一个满的队列会被阻塞,从空队列里面取元素也会被阻塞。


三 方法

挑选其中的三个记录一下。

  • void put(E e):插入元素,如果队列满则一直等待。插null会抛出NullPointerException,等待的时候被打断会抛出InterruptedException
  • boolean offer(E e,long timeout,TimeUnit unit):插入元素,成功返回true,队列满则不插入也不等待,返回false。就是说直接放弃了此次操作。后两个参数不解释了。
  • E take():拿出对头的元素,返回该元素,队列空一直等待,和put对应。


四 怎么用

我造了两个消费者和生产者,分别演示put和take的用法,以及offer和take的用法

  • put,这个由生产者调用
package ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

/**
 * BlockingQueue的使用
 * 生产者,生产方式:put
 * @author brucexiajun
 *
 */

public class Producer implements Runnable {

	private  BlockingQueue<Integer> blockingQueue;
	private static int element = 0;
	
	public Producer(BlockingQueue<Integer> blockingQueue) {
		this.blockingQueue = blockingQueue;
	}


	public void run() {
		try {
			while(element < 20) {
				System.out.println("将要放进去的元素是:"+element);
				blockingQueue.put(element++);
			}
		} catch (Exception e) {
			System.out.println("生产者在等待空闲空间的时候被打断了!");
			e.printStackTrace();
		}
		System.out.println("生产者已经终止了生产过程!");
	}
}

生产数字,从0一直到19,然后就停工了,中间如果消费者来不及消费,生产者会自动阻塞。

  • take,消费者调用
package ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable {

	private  BlockingQueue<Integer> blockingQueue;
	
	public Consumer(BlockingQueue<Integer> blockingQueue) {
		this.blockingQueue = blockingQueue;
	}


	public void run() {
		try {
			while(true) {
				System.out.println("取出来的元素是:"+blockingQueue.take());
			}
		} catch (Exception e) {
			System.out.println("消费者在等待新产品的时候被打断了!");
			e.printStackTrace();
		}
	}
}

消费者一直在消费,queue空的时候自动等待,即使生产者停止了生产,消费者也会等待。

  • 主函数所在的类
package ArrayBlockingQueue;

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

public class MainClass {

	public static void main(String[] args) {
		
		BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(3,true);
		Producer producerPut = new Producer(blockingQueue);
		Consumer consumer = new Consumer(blockingQueue);
		ProducerOffer producerOffer = new ProducerOffer(blockingQueue);
		
		new Thread(producerPut).start();
		
		new Thread(consumer).start();
		
		
	}
}

我新建了大小为3的队列,把这个队列传给生产者和消费者,它们公用这个队列,满的时候生产者阻塞,空的时候消费者阻塞,然后开启生产者和消费者进程。

运行结果如下:(每次不一样)

将要放进去的元素是:0
将要放进去的元素是:1
将要放进去的元素是:2
取出来的元素是:0
将要放进去的元素是:3
取出来的元素是:1
将要放进去的元素是:4
取出来的元素是:2
将要放进去的元素是:5
取出来的元素是:3
将要放进去的元素是:6
取出来的元素是:4
将要放进去的元素是:7
取出来的元素是:5
将要放进去的元素是:8
将要放进去的元素是:9
取出来的元素是:6
将要放进去的元素是:10
取出来的元素是:7
将要放进去的元素是:11
取出来的元素是:8
将要放进去的元素是:12
将要放进去的元素是:13
取出来的元素是:9
取出来的元素是:10
将要放进去的元素是:14
取出来的元素是:11
将要放进去的元素是:15
取出来的元素是:12
将要放进去的元素是:16
取出来的元素是:13
将要放进去的元素是:17
取出来的元素是:14
将要放进去的元素是:18
取出来的元素是:15
将要放进去的元素是:19
取出来的元素是:16
取出来的元素是:17
生产者已经终止了生产过程!
取出来的元素是:18
取出来的元素是:19

生产者最多连续生产3次,然后队列满了,要等待消费者消费,消费者同理

  • offer,生产者调用,把生产者的生产方式改成offer(用下面的类替换Producer类),如下
package ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

/**
 * BlockingQueue的使用
 * 生产者,生产方式:offer
 * @author brucexiajun
 *
 */

public class ProducerOffer implements Runnable {

	private  BlockingQueue<Integer> blockingQueue;
	private static int element = 0;
	
	
	public ProducerOffer(BlockingQueue<Integer> blockingQueue) {
		this.blockingQueue = blockingQueue;
	}


	public void run() {
		try {
			while(element < 20) {
				System.out.println("将要放进去的元素是:"+element);
				blockingQueue.offer(element++);
			}
		} catch (Exception e) {
			System.out.println("生产者在等待空闲空间的时候被打断了!");
			e.printStackTrace();
		}
		System.out.println("生产者已经终止了生产过程!");
	}
}

主类修改为

package ArrayBlockingQueue;

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

public class MainClass {

	public static void main(String[] args) {
		
		BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(3,true);
		Producer producerPut = new Producer(blockingQueue);
		Consumer consumer = new Consumer(blockingQueue);
		ProducerOffer producerOffer = new ProducerOffer(blockingQueue);
	
		
		new Thread(producerOffer).start();
		for(int i=0;i<10000;i++) {
			int a = i*2312/234*12;
		}
		new Thread(consumer).start();
		
		
	}
}

再次运行,结果为

将要放进去的元素是:0
将要放进去的元素是:1
将要放进去的元素是:2
将要放进去的元素是:3
将要放进去的元素是:4
将要放进去的元素是:5
将要放进去的元素是:6
将要放进去的元素是:7
将要放进去的元素是:8
取出来的元素是:0
将要放进去的元素是:9
将要放进去的元素是:10
取出来的元素是:1
将要放进去的元素是:11
取出来的元素是:2
将要放进去的元素是:12
取出来的元素是:8
将要放进去的元素是:13
取出来的元素是:9
将要放进去的元素是:14
取出来的元素是:11
将要放进去的元素是:15
取出来的元素是:12
将要放进去的元素是:16
取出来的元素是:13
将要放进去的元素是:17
取出来的元素是:14
将要放进去的元素是:18
取出来的元素是:15
将要放进去的元素是:19
取出来的元素是:16
生产者已经终止了生产过程!
取出来的元素是:17
取出来的元素是:18
取出来的元素是:19

取出来的元素0,1,2,然后直接是8,因为3-7根本没有放到队列里面,offer不会自己阻塞,会直接跳过这个插入的过程。

这个和put不一样,put会一直等待,就是说程序会一直停留在blockingQueue.put那一句,offer会跳过blockingQueue.offer那一句,而进入下一个while循环,这样实际插入队列的数字就会不连续了。

主函数里面的for循环是为了让生产者和消费者隔开一段时间,以展示offer的效果。

ArrayBlockingQueue的方法很多,参考API即可。