LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE
//生产者消费者模式: ArrayBlockingQueue有界队列
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
new Thread(() -> producer.m()).start();
new Thread(() -> consumer.m()).start();
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Producer {
protected BlockingQueue<String> queue = null;
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
public void m() {
try {
boolean b1 = queue.offer("1");
// Thread.sleep(1000);
boolean b2 = queue.offer("11");
Thread.sleep(1000);
boolean b3 = queue.offer("2");
Thread.sleep(1000);
boolean b4 = queue.offer("3");
System.out.println(b1 + " " + b2 + " " + b3 + " " + b4 + " ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer {
protected BlockingQueue<?> queue = null;
public Consumer(BlockingQueue<?> queue) {
this.queue = queue;
}
public void m() {
try {
System.out.println(queue.take() + "--1");
System.out.println(queue.take() + "--2");
System.out.println(queue.take() + "--3");
System.out.println(queue.take() + "--4");// 若队列中没有数据了,会阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
offer方法不会阻塞,如果不能插入队列直接返回,有可能造成数据丢失。比如上面的例子,队列长度为1,11是会丢失掉,而put方法会阻塞,等待消费完有空位时,唤醒put线程。
相同的道理:获取元素,take方法是阻塞的,poll方法不是阻塞的。
public void doLoan() {
logger.debug("NoticeTask start");
while (NoticeJobQueue.NOTICE != null && NoticeJobQueue.NOTICE.size() > 0) {
Notice notice = NoticeJobQueue.NOTICE.peek();
if (notice != null) {
try {
noticeService.sendNotice(notice);
} catch (Exception e) {
logger.error(e.getMessage(), e);
e.printStackTrace();
} finally {
NoticeJobQueue.NOTICE.remove(notice);
}
}
}
}
对比各种queue(并发情景下使用的是最多的就是queue)
LinkedBlockingQueue 阻塞式队列中用put/take来存取,满了去put或者空了再take都会引起阻塞等待,自动实现阻塞是生产者消费者,它是无界队列,可以容纳无限个元素,只要内存可以支撑。但是消费者一直在消费是不会满的
ArrayBlockingQueue 有界队列,装的是一个一个的任务,在线程池里用的比较多,api包括offer,add,peek等等
DelayQueue 无界队列,装的也是一个一个任务,默认是按添加时间排好顺序的,等待时间最长的排在最前,任务记录了还有多长时间可以被消费,可以用来做定时任务。任务需要实现Delayed接口,而他又是从Comparable接口继承的
LinkedTransferQueue 一般用在实时的消息处理,必须先启动消费者,在调用transfer(),否则就会阻塞
SynchronousQueue 是一个容量为0的特殊的TranferQueue,内部调用的tranfer()来执行,任何一个元素必须立刻交给消费者消费。transfer不会加到队列里,直接交给消费者
CopyOnWrite 实际业务中写的很少,读的特别多的情况使用。
ConcurrentLinkedQueue全是采用的非阻塞算法,里面没有使用任何锁,全是基于CAS操作实现的,这样来保证元素的一致性。CAS操作可以说是JAVA并发框架的基础,整个框架的设计都是基于CAS操作的。