线程池之工作队列

ArrayBlockingQueue

采用数组来实现,并采用可重入锁ReentrantLock来做并发控制,无论是添加还是读取,都先要获得锁才能进行操作 可看出进行读写操作都使用了ReentrantLock,ArrayBlockingQueue需要为其指定容量

  1. public boolean offer(E e) {
  2. checkNotNull(e);
  3. final ReentrantLock lock = this.lock;
  4. lock.lock();
  5. try {
  6. if (count == items.length)
  7. return false;
  8. else {
  9. enqueue(e);
  10. return true;
  11. }
  12. } finally {
  13. lock.unlock();
  14. }
  15. }
  16.  
  17. public void put(E e) throws InterruptedException {
  18. checkNotNull(e);
  19. final ReentrantLock lock = this.lock;
  20. lock.lockInterruptibly();
  21. try {
  22. while (count == items.length)
  23. notFull.await();
  24. enqueue(e);
  25. } finally {
  26. lock.unlock();
  27. }
  28. }

SynchronousQueue

由于SynchronousQueue源码比较复杂,里面大量的Cas操作,SynchronousQueue没有容器,所以里面是装不了任务的,当一个生产者线程生产一个任务的 时候,如果没有对应的消费者消费,那么该生产者会一直阻塞,知道有消费者消费为止。
图示:
 如下代码,如果我们将消费者线程注释掉执行,那么生产者哪里将会一直阻塞

  1. package thread.customthreadpool;
  2.  
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.SynchronousQueue;
  6. import java.util.concurrent.ThreadPoolExecutor;
  7.  
  8. /**
  9. * 测试SynchronousQueue
  10. */
  11. public class SynchronousQueueTest {
  12.  
  13. private static final SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
  14.  
  15. private static final ExecutorService service = Executors.newCachedThreadPool();
  16.  
  17. public static void main(String[] args) {
  18. /**
  19. * Provider
  20. */
  21. service.submit(() -> {
  22. try {
  23. synchronousQueue.put("liu");
  24. }catch (Exception e){
  25. e.printStackTrace();
  26. }
  27. System.out.println("Consumer finished spending");
  28. });
  29.  
  30. /**
  31. * Consumer
  32. */
  33. service.submit(() ->{
  34. try {
  35. synchronousQueue.take();
  36. }catch (Exception e){
  37. e.printStackTrace();
  38. }
  39. System.out.println("take over");
  40. });
  41. }
  42. }

LinkedBlockingDeque

LinkedBlockingDeque是一个双向队列,底层使用单链表实现,任何一段都可进行元素的读写操作,在初始化LinkedBlockingDeque的时候, 我们可以指定容量,也可不指定,如果不指定,则容量为Integer.MAX_VALUE,

注:Deque是双端队列,而Queue是单端队列,双端意思是两端都可以进行读写操作,而单端则只能从一端进,一端出(FIFO)
  1. public LinkedBlockingDeque() {
  2. this(Integer.MAX_VALUE);
  3. }
  1. package thread.customthreadpool;
  2. import java.util.concurrent.LinkedBlockingDeque;
  3. public class LinkedBlockingDequeTest {
  4.  
  5. private static final LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>();
  6.  
  7. public static void main(String[] args) throws InterruptedException {
  8. deque.put(1);
  9. deque.put(2);
  10. deque.put(3);
  11. deque.put(4);
  12. deque.put(5);
  13. System.out.println(deque);
  14. System.out.println("deque size "+deque.size());
  15. deque.take();
  16. deque.take();
  17. deque.take();
  18. deque.take();
  19. deque.take();
  20. System.out.println(deque);
  21. System.out.println("deque size "+deque.size());
  22. }
  23. }
 

LinkedBlockingQueue

底层基于单向连表实现,是一个单向队列,具有先进先出(FIFO)特点,使用了ReentrantLock来做并发控制,读写操作都上锁

  1. private final ReentrantLock putLock = new ReentrantLock();
  2. public void put(E e) throws InterruptedException {
  3. if (e == null) throw new NullPointerException();
  4. int c = -1;
  5. Node<E> node = new Node<E>(e);
  6. final ReentrantLock putLock = this.putLock;
  7. final AtomicInteger count = this.count;
  8. putLock.lockInterruptibly();
  9. try {
  10. while (count.get() == capacity) {
  11. notFull.await();
  12. }
  13. enqueue(node);
  14. c = count.getAndIncrement();
  15. if (c + 1 < capacity)
  16. notFull.signal();
  17. } finally {
  18. putLock.unlock();
  19. }
  20. if (c == 0)
  21. signalNotEmpty();
  22. }
  23. public E take() throws InterruptedException {
  24. E x;
  25. int c = -1;
  26. final AtomicInteger count = this.count;
  27. final ReentrantLock takeLock = this.takeLock;
  28. takeLock.lockInterruptibly();
  29. try {
  30. while (count.get() == 0) {
  31. notEmpty.await();
  32. }
  33. x = dequeue();
  34. c = count.getAndDecrement();
  35. if (c > 1)
  36. notEmpty.signal();
  37. } finally {
  38. takeLock.unlock();
  39. }
  40. if (c == capacity)
  41. signalNotFull();
  42. return x;
  43. }

DelayDeque

DelayDeque是一个无界队列,添加进DelayDeque的元素会经过compareTo方法计算,然后按照时间 进行排序,排在队头的元素是最早到期的,越往后到期时间越长,DelayDeque只能接受Delayed接口类型 如图所示,队列里的元素并不是按照先进先出的规则,而是按照过期时间

示例
  1. package thread.customthreadpool.delayDeque;
  2.  
  3. import java.util.concurrent.Delayed;
  4. import java.util.concurrent.TimeUnit;
  5.  
  6. public class MyDelayed implements Delayed {
  7.  
  8. private final String taskName ;
  9. private final long nowTime = System.currentTimeMillis();
  10. private final long expireTime ;
  11.  
  12. public MyDelayed(String taskName,long expireTime) {
  13. this.taskName = taskName;
  14. this.expireTime = expireTime;
  15. }
  16.  
  17. @Override
  18. public long getDelay(TimeUnit unit) {
  19. return unit.convert((nowTime+expireTime) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
  20. }
  21.  
  22. @Override
  23. public int compareTo(Delayed o) {
  24. MyDelayed myDelayed = (MyDelayed) o;
  25. return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
  26. }
  27.  
  28. @Override
  29. public String toString() {
  30. return "MyDelayed{" +
  31. "taskName='" + taskName + '\'' +
  32. ", nowTime=" + nowTime +
  33. ", expireTime=" + expireTime +
  34. '}';
  35. }
  36. }
  1. package thread.customthreadpool.delayDeque;
  2.  
  3. import java.util.concurrent.*;
  4.  
  5. public class MyDelayQueue {
  6.  
  7. private static final DelayQueue<MyDelayed> delayQueue = new DelayQueue<>();
  8.  
  9. private static final ExecutorService service = Executors.newCachedThreadPool();
  10.  
  11. public static void main(String[] args) throws InterruptedException {
  12. service.submit(() -> {
  13. delayQueue.put(new MyDelayed("A-Task",5000));
  14. delayQueue.put(new MyDelayed("B-Task",4000));
  15. delayQueue.put(new MyDelayed("C-Task",3000));
  16. delayQueue.put(new MyDelayed("D-Task",2000));
  17. delayQueue.put(new MyDelayed("E-Task",1000));
  18. });
  19. while (true){
  20. System.out.println(delayQueue.take());
  21. }
  22. }
  23. }
 
result

应用场景

1.美团外卖订单:当我们下单后没付款 ,30分钟后将自动取消订单
2.缓存,对于某些任务,需要在特定的时间清理;
and so on

LinkedTransferQueue

当消费线程从队列中取元素时,如果队列为空,那么生成一个为null的节点,消费者线程就一直等待,此时如果生产者线程发现队列中有一个null节点, 它就不入队了,而是将元素填充到这个null节点并唤醒消费者线程,然后消费者线程取走元素。
LinkedTransferQueue是 SynchronousQueue 和 LinkedBlockingQueue 的整合,性能比较高,因为没有锁操作, SynchronousQueue不能存储元素,而LinkedTransferQueue能存储元素,

PriorityBlockingQueue

PriorityBlockingQueue是一个无界的阻塞队列,同时是一个支持优先级的队列,读写操作都是基于ReentrantLock, 内部使用堆算法保证每次出队都是优先级最高的元素

  1. public E take() throws InterruptedException {
  2. final ReentrantLock lock = this.lock;
  3. lock.lockInterruptibly();
  4. E result;
  5. try {
  6. while ( (result = dequeue()) == null)
  7. notEmpty.await();
  8. } finally {
  9. lock.unlock();
  10. }
  11. return result;
  12. }

java线程池-工作队列workQueue的更多相关文章

  1. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  2. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  3. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  4. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  5. [转 ]-- Java线程池使用说明

    Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...

  6. Java 线程池框架核心代码分析

    前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...

  7. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

  8. java 线程池 并行 执行

    https://github.com/donaldlee2008/JerryMultiThread/blob/master/src/com/jerry/threadpool/ThreadPoolTes ...

  9. Java线程池带图详解

    线程池作为Java中一个重要的知识点,看了很多文章,在此以Java自带的线程池为例,记录分析一下.本文参考了Java并发编程:线程池的使用.Java线程池---addWorker方法解析.线程池.Th ...

随机推荐

  1. 【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)

    问题描述 在Azure App Service上部署了站点,想要在网站的响应头中加一个字段(Cache-Control),并设置为固定值(Cache-Control:no-store) 效果类似于本地 ...

  2. 我为什么选Markdown

    前沿说明:Yaml Front Matter MarkDown 目录 前沿说明:Yaml Front Matter 什么是MarkDown Markdown是一种轻量级标记语言, 它允许人们使用易读易 ...

  3. 记录21.07.24 —— Vue的组件与路由

    VUE组件 作用:复用性 创建组件的三种方式 第一种:使用extends搭配component方法 第二种:直接使用component方法 只有用vue声明且命名的才称之为创建组件 注意:templa ...

  4. 自学linux——4.Linux的自有服务(基础篇)

    linux自有服务(内置) 一.运行级别(模式) 在Linux中存在一个进程:init,进程id是1. 查看进程:#ps -ef|grep init 对应的配置文件:inittab(运行级别配置文件位 ...

  5. 超详细!Vue-Router手把手教程

    目录 1,router-view 2,router-link 3,重定向redirect 4,路由别名 5,路由传参props 5.1,布尔模式 5.2,对象模式 5.3,函数模式 6,路由守卫 6. ...

  6. Java流程控制05——循环结构

    循环结构 while 循环  while(布尔表达式){   //循环语句 } 只要布尔表达式为true,循环就会一直执行下去. 我们为你大多数情况是会让循环停止下来的,我们需要让一个表达式时效的方式 ...

  7. C++ 读 ,写 文件

    1 //文件操作 2 //文本文件 读 ,写文件 3 4 #include <iostream> 5 #include <string> 6 #include<fstre ...

  8. 谈谈 Nginx 那点事【一】

    为什么突然决定总结Nginx ? 不知不觉8月份又要过完了,时间真是个无情的崽种. 写Nginx 首先,主要源于最近项目部署工作中和公司技术中心的人对接部署相关事宜流程太繁琐了.每个部门有各自的工作安 ...

  9. OpenCV 之 自定义滤波

    图像处理中,"空间域" 指的是图像平面,因此,空间滤波 可定义为:在图像平面内对像素灰度值进行的滤波 1  空间滤波 1.1  滤波过程 如图,Filter 是一个 3x3 滤波核 ...

  10. 初识nest.js

    nest的核心概念: Nest的核心概念是提供一种体系结构,它帮助开发人员实现层的最大分离,并在应用程序中增加抽象. 架构预览: 主要有三个核心概念:模块Module,  控制器Controller, ...