BlockingQueue(阻塞队列)是java中常见的容器,在多线程编程中被广泛使用。 当队列容器已满时生产者线程被阻塞,直到队列未满后才可以继续put; 当队列容器为空时,消费者线程被阻塞,直至队列非空时才可以继续take。
阻塞队列,顾名思义就是线程使用队列时会阻塞当前线程;BlockingQueue 继承了Collection,具有一般集合所具有的数据存取功能;是线程安全的队列,多线程访问时不会出现同一个数据集中的数据被多次取出,或者覆盖存放的事件。使用场景,可用于一个快速反馈的消息队列,无消息时阻塞线程让出CPU,有数据存入时通知线程取出数据,取完后继续阻塞。
内部通过ReentrantLock实现。 ReentrantLock,通常翻译为再入锁,是 Java 5 提供的锁实现,它的语义和 synchronized 基本相同。再入锁通过代码直接调用 lock() 方法获取,代码书写也更加灵活。与此同时,ReentrantLock 提供了很多实用的方法,能够实现很多 synchronized 无法做到的细节控制,比如可以控制 fairness,也就是公平性,或者利用定义条件等。但是,编码中也需要注意,必须要明确调用 unlock() 方法释放,不然就会一直持有该锁。
package interviews.patterns;
public class Task {
public String id;
public Task(String name){
this.id = name;
}
}
package interviews.patterns;
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable{
private BlockingQueue<Task> blockingQueue;
public Consumer(BlockingQueue<Task> queue){
this.blockingQueue = queue;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(200);
Task task = blockingQueue.take();
System.out.println("consumer get one task " + task.id);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package interviews.patterns;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
BlockingQueue<Task> blockingQueue;
public Producer(BlockingQueue<Task> queue) {
this.blockingQueue = queue;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(200);
if(blockingQueue.remainingCapacity() > 0) {
Task task = new Task(UUID.randomUUID().toString());
blockingQueue.add(task);
System.out.println("producer add a new task = " + task.id);
}else{
System.out.println("producer is full");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void test(){
BlockingQueue<Task> blockingQueue = new ArrayBlockingQueue<Task>(10);
Thread producerThread = new Thread( new Producer(blockingQueue));
Thread consumerThread = new Thread( new Consumer(blockingQueue));
producerThread.start();
consumerThread.start();
}
}