在日常开发中,我们经常会遇到在高并发的情况下,对于瞬间产生的大量数据来不及处理,这时候该怎么办?总不能数据还没处理就把数据丢弃,那我们该把这些数据存储在哪呢?简单的数组或者util包下的集合类型是不能满足在这种并发情况下存储数据要求的。这时候就得我们自己定义这么一种队列,它能在多线程环境下,当本身没有存储数据或者存满数据的情况下,能够让相应线程阻塞,一旦上面的条件不成立时,可以立马唤醒相应线程去存数据或者取数据。要想实现这种需求的数据容器,对于新手程序员尤其像我这样的菜鸟实属不易,还好jdk已经帮我们实现了这么一种类型,它就是LinkedBlockingQueue,今天我们就一起来看下它的源码,看能不能给大家一些启发。
//阻塞队列内部存储数据的节点定义
static class Node<E> {
volatile E item;
Node<E> next;
Node(E x) { item = x; }
}
LinkedBlockingQueue内部数据是通过Node类以链表形式组织的。
通过下面几个属性实现线程同步。
//当前队列数目统计
private final AtomicInteger count = new AtomicInteger(0);
private final ReentrantLock takeLock = new ReentrantLock();
//非空条件
private final Condition notEmpty = takeLock.newCondition();
private final ReentrantLock putLock = new ReentrantLock();
//非满条件
private final Condition notFull = putLock.newCondition();
现在我们来看下LinkedBlockingQueue的put方法实现逻辑,了解当数据入队时其会做哪些操作。
public void put(E e) throws InterruptedException {
//可见阻塞队列是不能存放空值的
if (e == null) throw new NullPointerException();
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
//加锁,防止多线程同时操作数据
putLock.lockInterruptibly();
try {
//当队列已满,则线程阻塞
while (count.get() == capacity) {
notFull.await();
}
//创建一个节点,并把它链接在对尾
enqueue(e);
c = count.getAndIncrement();
//当队列有空余空间,发出信号,唤醒休眠线程
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
//当c==0时,表示队列已经添加新元素,此时发出非空信号
if (c == 0)
signalNotEmpty();
}
数据出队时
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
//开启线程同步
takeLock.lockInterruptibly();
try {
//当队列为空时,非空条件发出对取数线程的等待信号
while (count.get() == 0) {
//插入线程释放锁进入等待
notEmpty.await();
}
//取出队首元素
x = dequeue();
//计数器减一
c = count.getAndDecrement();
//当元素数量大于一时,通知等待在notEmpty上的线程,数组中有新的元素可以操作
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
//计数变量c等于队列容量时说明队列已有元素出列,则此时发出队列空缺信号,唤醒线程
if (c == capacity)
signalNotFull();
return x;
}
介绍完阻塞队列的实现原理,下面提供个代码案例,讲下阻塞队列在生产者、消费者应用环境下如何使用
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 实现一个类似于消息队列的例子,消息来了进行处理,否则线程等待,直到消息的到来
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 消费线程
* @author lujunfa
*
*/
class MyCustomer extends Thread{
LinkedBlockingQueue<String> queue;
public MyCustomer(LinkedBlockingQueue<String> queue) {
// TODO Auto-generated constructor stub
this.queue = queue;
}
@Override
public void run() {
while(true){
try {
String res = queue.take();
System.out.println(res);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 生产线程
* @author lujunfa
*
*/
class MyProducer extends Thread{
LinkedBlockingQueue<String> queue;
public MyProducer(LinkedBlockingQueue<String> queue) {
// TODO Auto-generated constructor stub
this.queue = queue;
}
@Override
public void run() {
while(true){
try {
queue.put("lujunfa");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class LinkedBlockingQueueDemon {
public static void main(String[] args) {
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();
new MyCustomer(queue).start();
new MyProducer(queue).start();
/*Scanner scan = new Scanner(System.in);
String temp ="sadasd";
while(!"no".equalsIgnoreCase(temp)){
try {
queue.put(temp);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
temp = scan.next();
}*/
}
}