基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
ArrayBlockingQueue和LinkedBlockingQueue是两个最普通也是最常用的阻塞队列,一般情况下,在处理多线程间的生产者消费者问题,使用这两个类足以。
下面的代码演示了如何使用BlockingQueue:
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName: 生产者线程
* @Description:
*/
public class Producer implements Runnable {
private volatile boolean isRunning = true;
private BlockingQueue<String> queue;
private static AtomicInteger count = new AtomicInteger();
private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
String data = null;
Random r = new Random();
System.out.println("启动生产者线程!");
try {
while (isRunning) {
System.out.println("正在生产数据...");
Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));
data = "data:" + count.incrementAndGet();
System.out.println("将数据:" + data + "放入队列...");
if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
System.out.println("放入数据失败:" + data);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} finally {
System.out.println("退出生产者线程!");
}
}
public void stop() {
isRunning = false;
}
}
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @ClassName: 消费者线程
* @Description:
*/
public class Consumer implements Runnable {
private BlockingQueue<String> queue;
private static final int DEFAULT_RANGE_FOR_SLEEP = 1000;
public Consumer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
System.out.println("启动消费者线程!");
Random r = new Random();
boolean isRunning = true;
try {
while (isRunning) {
System.out.println("正从队列获取数据...");
String data = queue.poll(2, TimeUnit.SECONDS);
if (null != data) {
System.out.println("拿到数据:" + data);
System.out.println("正在消费数据:" + data);
Thread.sleep(r.nextInt(DEFAULT_RANGE_FOR_SLEEP));
} else {
// 超过2s还没数据,认为所有生产线程都已经退出,自动退出消费线程。
isRunning = false;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
} finally {
System.out.println("退出消费者线程!");
}
}
}
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @ClassName: 测试类
* @Description:
*/
public class BlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
// 声明一个容量为10的缓存队列
BlockingQueue<String> queue = new LinkedBlockingQueue<String>(10);
Producer producer1 = new Producer(queue);
Producer producer2 = new Producer(queue);
Producer producer3 = new Producer(queue);
Consumer consumer = new Consumer(queue);
// 借助Executors
ExecutorService service = Executors.newCachedThreadPool();
// 启动线程
service.execute(producer1);
service.execute(producer2);
service.execute(producer3);
service.execute(consumer);
// 执行10s
Thread.sleep(10 * 1000);
producer1.stop();
producer2.stop();
producer3.stop();
Thread.sleep(2000);
// 退出Executor
service.shutdown();
}
}
运行结果:
启动生产者线程!
正在生产数据...
启动生产者线程!
正在生产数据...
启动生产者线程!
正在生产数据...
启动消费者线程!
正从队列获取数据...
将数据:data:1放入队列...
拿到数据:data:1
正在消费数据:data:1
正在生产数据...
将数据:data:2放入队列...
正在生产数据...
将数据:data:3放入队列...
正在生产数据...
正从队列获取数据...
拿到数据:data:2
正在消费数据:data:2
将数据:data:4放入队列...
正在生产数据...
将数据:data:5放入队列...
正在生产数据...
将数据:data:6放入队列...
正在生产数据...
将数据:data:7放入队列...
正在生产数据...
将数据:data:8放入队列...
正在生产数据...
将数据:data:9放入队列...
正在生产数据...
将数据:data:10放入队列...
正在生产数据...
正从队列获取数据...
拿到数据:data:3
正在消费数据:data:3
将数据:data:11放入队列...
正在生产数据...
将数据:data:12放入队列...
正在生产数据...
正从队列获取数据...
拿到数据:data:4
正在消费数据:data:4
正从队列获取数据...
拿到数据:data:5
正在消费数据:data:5
将数据:data:13放入队列...
正在生产数据...
将数据:data:14放入队列...
正在生产数据...
将数据:data:15放入队列...
正在生产数据...
将数据:data:16放入队列...
正从队列获取数据...
正在生产数据...
拿到数据:data:6
正在消费数据:data:6
将数据:data:17放入队列...
将数据:data:18放入队列...
将数据:data:19放入队列...
正从队列获取数据...
拿到数据:data:7
正在消费数据:data:7
正在生产数据...
将数据:data:20放入队列...
正从队列获取数据...
正在生产数据...
拿到数据:data:8
正在消费数据:data:8
将数据:data:21放入队列...
正从队列获取数据...
正在生产数据...
拿到数据:data:9
正在消费数据:data:9
将数据:data:22放入队列...
正从队列获取数据...
正在生产数据...
拿到数据:data:10
正在消费数据:data:10
将数据:data:23放入队列...
放入数据失败:data:21
正在生产数据...
正从队列获取数据...
正在生产数据...
拿到数据:data:11
正在消费数据:data:11
将数据:data:24放入队列...
将数据:data:25放入队列...
正从队列获取数据...
拿到数据:data:12
正在消费数据:data:12
正在生产数据...
正从队列获取数据...
正在生产数据...
拿到数据:data:13
正在消费数据:data:13
正从队列获取数据...
拿到数据:data:14
正在消费数据:data:14
正在生产数据...
正从队列获取数据...
拿到数据:data:15
正在消费数据:data:15
将数据:data:26放入队列...
正在生产数据...
将数据:data:27放入队列...
将数据:data:28放入队列...
将数据:data:29放入队列...
正从队列获取数据...
拿到数据:data:16
正在消费数据:data:16
正在生产数据...
正从队列获取数据...
拿到数据:data:17
正在消费数据:data:17
正在生产数据...
将数据:data:30放入队列...
将数据:data:31放入队列...
正从队列获取数据...
拿到数据:data:18
正在消费数据:data:18
退出生产者线程!
正从队列获取数据...
拿到数据:data:19
退出生产者线程!
正在消费数据:data:19
放入数据失败:data:31
退出生产者线程!
正从队列获取数据...
拿到数据:data:20
正在消费数据:data:20
正从队列获取数据...
拿到数据:data:22
正在消费数据:data:22
正从队列获取数据...
拿到数据:data:23
正在消费数据:data:23
正从队列获取数据...
拿到数据:data:24
正在消费数据:data:24
正从队列获取数据...
拿到数据:data:25
正在消费数据:data:25
正从队列获取数据...
拿到数据:data:26
正在消费数据:data:26
正从队列获取数据...
拿到数据:data:27
正在消费数据:data:27
正从队列获取数据...
拿到数据:data:28
正在消费数据:data:28
正从队列获取数据...
拿到数据:data:29
正在消费数据:data:29
正从队列获取数据...
拿到数据:data:30
正在消费数据:data:30
正从队列获取数据...
退出消费者线程!
运行结果不固定