Java线程池的任务消息队列
多线程队列
Java多线程包括线程池会用到缓存任务的队列,Java提供的线程安全队列分为两种:阻塞队列和非阻塞队列
1.阻塞队列
阻塞队列支持生产者模式和消费者模式互相等待,队列为空,消费线程阻塞,直到队列不为空;当队列满时,生产线程会阻塞,直到队列不满。
Java ThreadPool中也用到阻塞队列,当创建的线程数超过核心线程数,新提交的任务会被push到阻塞队列中。根据自己的业务可以选择不同的队列。
队列类型 | 说明 |
ArrayBlockingQueue |
|
LinkedBlockingQueue |
|
PriorityBlockingQueue |
|
DelayQueue |
|
SynchronousQueue |
|
Java线程池Executors实现的四种类型的ThreadPoolExecutor对应上面的缓存队列详情如下:
线程池 | 实现队列 |
newCachedThreadPool | SynchronousQueue |
newFixedThreadPool | LinkedBlockingQueue |
newScheduledThreadPool | DelayQueue |
newSingleThreadExecutor | LinkedBlockingQueue |
2.非阻塞队列
常用的非阻塞线程安全队列是ConcurrentLinkedQueue,一种无界限队列。FIFO原则,基于链表实现,CAS乐观锁保证线程安全。
构造函数:
- head、tail节点组成
- 每个节点(Node)由节点元素(item)和指向下一个节点的引用 (next) 组成
- 节点与节点之间通过 next 关联
- 队列初始化时, head 节点存储的元素为空,tail 节点等于 head 节点
public ConcurrentLinkedQueue() {head = tail = new Node<E>(null);
}private static class Node<E> {volatile E item;volatile Node<E> next;..
}
入列:一个线程入列一个数据时,会将该数据封装成一个 Node 节点,并先获取到队列的队尾节点,当确定此时队尾节点的 next 值为 null 之后,再通过 CAS 将新队尾节点的 next 值设为新节点。此时 p != t,也就是设置 next 值成功,然后再通过 CAS 将队尾节点设置为当前节点即可。
public boolean offer(E e) {checkNotNull(e);//创建入队节点final Node<E> newNode = new Node<E>(e);//t,p为尾节点,默认相等,采用失败即重试的方式,直到入队成功 for (Node<E> t = tail, p = t;;) {//获取队尾节点的下一个节点Node<E> q = p.next;//如果q为null,则代表p就是队尾节点if (q == null) {//将入列节点设置为当前队尾节点的next节点if (p.casNext(null, newNode)) {//判断tail节点和p节点距离达到两个节点if (p != t) // hop two nodes at a time//如果tail不是尾节点则将入队节点设置为tail。// 如果失败了,那么说明有其他线程已经把tail移动过 casTail(t, newNode); // Failure is OK.return true;}}// 如果p节点等于p的next节点,则说明p节点和q节点都为空,表示队列刚初始化,所以返回 else if (p == q)p = (t != (t = tail)) ? t : head;else// Check for tail updates after two hops.p = (p != t && t != (t = tail)) ? t : q;}}
出列: 首先获取 head 节点,并判断 item 是否为 null,如果为空,则表示已经有一个线程刚刚进行了出列操作,然后更新 head 节点;如果不为空,则使用 CAS 操作将 head 节点设置为 null,CAS 就会成功地直接返回节点元素,否则还是更新 head 节点
public E poll() {// 设置起始点restartFromHead:for (;;) {//p获取head节点for (Node<E> h = head, p = h, q;;) {//获取头节点元素E item = p.item;//如果头节点元素不为null,通过cas设置p节点引用的元素为nullif (item != null && p.casItem(item, null)) {// Successful CAS is the linearization point// for item to be removed from this queue.if (p != h) // hop two nodes at a timeupdateHead(h, ((q = p.next) != null) ? q : p);return item;}//如果p节点的下一个节点为null,则说明这个队列为空,更新head结点else if ((q = p.next) == null) {updateHead(h, p);return null;}//节点出队失败,重新跳到restartFromHead来进行出队else if (p == q)continue restartFromHead;elsep = q;}}}
结论:ConcurrentLinkedQueue 是基于 CAS 乐观锁实现的,在并发时的性能要好于其它阻塞队列,因此很适合作为高并发场景下的排队队列。
Disruptor
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题。
为什么性能高?
- CAS
- 消除伪共享
- RingBuffer
最后
我们看到Executors提供的很多默认方法使用的都是无界的LinkedBlockingQueue,高负载的情况下无界队列很容易导致OOM,OOM会导致stop world,这是致命的,所以不建议使用Executors。建议使用使用了有界队列的线程池。
当没有设置拒绝策略时会抛出RejectedExecutionException运行时异常,并不会强制抛出,所以任务比较重要时,务必要自己实现拒绝策略。自定义拒绝策略往往和降级策略(重要的任务可以存入数据库或消息队列,启用另外的补偿线程池去做消费)搭配使用。
Java线程池的任务消息队列相关推荐
- JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue .
2019独角兽企业重金招聘Python工程师标准>>> 从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.Thread ...
- 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue
从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...
- JAVA线程池之双端队列与工作密取workstealingpool java7新加的一种线程池
https://blog.csdn.net/tjbsl/article/details/98480843
- Java线程池实现多消费者批量处理队列消息
通常生产者-消费者的经典实现方式是,启动一个消费者线程从阻塞队列里获取消息进行消费,(多个)生产者往队列里插入待消费的数据然后立即返回.如果生产者生产的速率远大于消费者消费的速率,那么队列的待处理数据 ...
- java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...
线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...
- Java 线程池原理和队列详解
转载请标明出处:http://blog.csdn.net/xx326664162/article/details/51701508 文章出自:薛瑄的博客 你也可以查看我的其他同类文章,也会让你有一定的 ...
- Java线程池和阻塞队列
一.Java线程池的优点 1.降低资源消耗:通过重复利用线程池中已创建好的线程来降低线程创建和销毁造成的消耗. 2.提高响应速度:当任务到达时,任务可以直接拿到线程池中已创建好的线程立即执行. 3.提 ...
- java线程池队列场景,Java面试题汇总
01 并发宝典:面试专题 面试专题分为四个部分,分别如下 Synchronized 相关问题 可重入锁 ReentrantLock 及其他显式锁相关问题 Java 线程池相关问题 Java 内存模型相 ...
- 面试必问---Java线程池8大拒绝策略
前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...
最新文章
- Python游戏开发,pygame模块,Python实现愤怒的小鸟【附带源码】
- php+mysql案例含源码_【专注】Zabbix源码安装教程—步骤详解(1)安装前准备
- python如何实现办公自动化培训_基于python实现自动化办公学习笔记(CSV、word、Excel、PPT)...
- apache camel_Apache Camel简介
- HDU 5392 BC #51
- win10启动项_win10 -- 取消不需要的开机启动项和服务项加快win10系统开机速度
- Hash碰撞的解决方案
- 如何禁止TextBox的记忆功能
- java数据结构和算法
- 计算机组装主板,组装电脑如何选择合适的主板 组装电脑选择合适主板介绍【详解】...
- 33个网站足以使你成为一个天才
- vue实现大转盘抽奖
- XP安装SQL2000个人版
- PRD 算法 Golang 实现
- 【rust】part-7 self,crate,super、use,as
- 加字邮票价格_中华人民共和国邮票(加字改值邮票)
- 中国联通物联网平台能力介绍
- 开关量、数字量、模拟量、脉冲量的区别
- 图片滑块验证码的解决
- Call Indels/SV常用软件-搬运工