多线程队列

Java多线程包括线程池会用到缓存任务的队列,Java提供的线程安全队列分为两种:阻塞队列非阻塞队列

1.阻塞队列

阻塞队列支持生产者模式和消费者模式互相等待,队列为空,消费线程阻塞,直到队列不为空;当队列满时,生产线程会阻塞,直到队列不满。

Java ThreadPool中也用到阻塞队列,当创建的线程数超过核心线程数,新提交的任务会被push到阻塞队列中。根据自己的业务可以选择不同的队列。

阻塞队列
队列类型 说明
ArrayBlockingQueue
  • 基于数组
  • FIFO(先进先出)原则对元素排序
  • ReentrantLock、Condition 实现线程安全
LinkedBlockingQueue
  • 基于链表
  • FIFO原则对元素排序
  • ReentrantLock、Condition 实现线程安全
  • 吞吐量高于ArrayBlockingQueue
PriorityBlockingQueue
  • 基于二叉树实现的无界限(最大值Integer.MAX_VALUE - 8)阻塞队列
  • 有优先级的无限阻塞队列
  • 无排序
  • 数据变更时,将最小或最大数据放在最上面节点上
  • ReentrantLock、Condition 实现的线程安全
DelayQueue
  • 支持延时获取元素的无界阻塞队列
  • 基于 PriorityBlockingQueue 扩展实现
  • 实现了Delay 延时接口
SynchronousQueue
  • 不存储多个元素的阻塞队列
  • 放入元素时要等相应的消费者取走元素才可放入
  • 使用两种模式管理数据:1.后进先出 2.先进先出

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线程池的任务消息队列相关推荐

  1. JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue .

    2019独角兽企业重金招聘Python工程师标准>>> 从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.Thread ...

  2. 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

    从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...

  3. JAVA线程池之双端队列与工作密取workstealingpool java7新加的一种线程池

    https://blog.csdn.net/tjbsl/article/details/98480843

  4. Java线程池实现多消费者批量处理队列消息

    通常生产者-消费者的经典实现方式是,启动一个消费者线程从阻塞队列里获取消息进行消费,(多个)生产者往队列里插入待消费的数据然后立即返回.如果生产者生产的速率远大于消费者消费的速率,那么队列的待处理数据 ...

  5. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  6. Java 线程池原理和队列详解

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/51701508 文章出自:薛瑄的博客 你也可以查看我的其他同类文章,也会让你有一定的 ...

  7. Java线程池和阻塞队列

    一.Java线程池的优点 1.降低资源消耗:通过重复利用线程池中已创建好的线程来降低线程创建和销毁造成的消耗. 2.提高响应速度:当任务到达时,任务可以直接拿到线程池中已创建好的线程立即执行. 3.提 ...

  8. java线程池队列场景,Java面试题汇总

    01 并发宝典:面试专题 面试专题分为四个部分,分别如下 Synchronized 相关问题 可重入锁 ReentrantLock 及其他显式锁相关问题 Java 线程池相关问题 Java 内存模型相 ...

  9. 面试必问---Java线程池8大拒绝策略

    前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...

最新文章

  1. ionic3中使用自定义配置
  2. Zend Studio 如何配置本地apache服务器使用xdebug调试php脚本
  3. Cookie获取用户的访问记录
  4. Java迭代器ListIterator
  5. 模型融合(stackingblending)
  6. 多行并一行显示的两种方式(DB2)
  7. Scrapy爬虫报错AttributeError: ‘NoneType‘ object has no attribute ‘write‘
  8. matlab聚合矩阵,c#和ILNumerics与Matlab中最有效的矩阵聚合方式
  9. [译]GLUT教程 - 整合代码2
  10. 2048小程序代码c语言,微信小程序版2048
  11. 我的世界java版种子掠夺者,《我的世界》:手机版中那些神级种子,各式地形一应俱全...
  12. 印象笔记 离线版_印象笔记离线版
  13. 【C#】动态数字时钟和日历
  14. Ueditor编辑器插入外链视频支持手机播放【iframe标签】
  15. vmware启动虚拟机报“内部错误”的解决方法
  16. vue 前端进行tab页面切换时,要求不刷新
  17. 重阳节,让我们祝天下的老年人生活幸福快乐。
  18. android 键盘回车按钮事件,Android中ENTER键(确认键)点击响应
  19. 16位汇编语言第二讲系统调用原理,以及各个寄存器详解
  20. Cocos合成大西瓜案例-下

热门文章

  1. 没有美术基础如何学?月薪过万建模大佬的八字真言
  2. 中央广播电视总台4K超高清频道国庆开播
  3. 在线Excel转HtmlTable工具
  4. 使用组策略禁止远程桌面资源重定向
  5. 用MATLAB画混淆矩阵 --2018/11/24
  6. 实用网页制作工具集锦
  7. 编程规范 (百度、华为)
  8. 如何制作绿盾终端静默安装包
  9. 加工中心编程技术讲座文库
  10. 求知讲堂python+人工智能day11