java线程池-工作队列workQueue
线程池之工作队列

ArrayBlockingQueue
采用数组来实现,并采用可重入锁ReentrantLock来做并发控制,无论是添加还是读取,都先要获得锁才能进行操作 可看出进行读写操作都使用了ReentrantLock,ArrayBlockingQueue需要为其指定容量
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
SynchronousQueue
由于SynchronousQueue源码比较复杂,里面大量的Cas操作,SynchronousQueue没有容器,所以里面是装不了任务的,当一个生产者线程生产一个任务的 时候,如果没有对应的消费者消费,那么该生产者会一直阻塞,知道有消费者消费为止。
图示:
如下代码,如果我们将消费者线程注释掉执行,那么生产者哪里将会一直阻塞
package thread.customthreadpool; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor; /**
* 测试SynchronousQueue
*/
public class SynchronousQueueTest { private static final SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>(); private static final ExecutorService service = Executors.newCachedThreadPool(); public static void main(String[] args) {
/**
* Provider
*/
service.submit(() -> {
try {
synchronousQueue.put("liu");
}catch (Exception e){
e.printStackTrace();
}
System.out.println("Consumer finished spending");
}); /**
* Consumer
*/
service.submit(() ->{
try {
synchronousQueue.take();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("take over");
});
}
}
LinkedBlockingDeque
LinkedBlockingDeque是一个双向队列,底层使用单链表实现,任何一段都可进行元素的读写操作,在初始化LinkedBlockingDeque的时候, 我们可以指定容量,也可不指定,如果不指定,则容量为Integer.MAX_VALUE,
注:Deque是双端队列,而Queue是单端队列,双端意思是两端都可以进行读写操作,而单端则只能从一端进,一端出(FIFO)
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
package thread.customthreadpool;
import java.util.concurrent.LinkedBlockingDeque;
public class LinkedBlockingDequeTest { private static final LinkedBlockingDeque<Integer> deque = new LinkedBlockingDeque<>(); public static void main(String[] args) throws InterruptedException {
deque.put(1);
deque.put(2);
deque.put(3);
deque.put(4);
deque.put(5);
System.out.println(deque);
System.out.println("deque size "+deque.size());
deque.take();
deque.take();
deque.take();
deque.take();
deque.take();
System.out.println(deque);
System.out.println("deque size "+deque.size());
}
}

LinkedBlockingQueue
底层基于单向连表实现,是一个单向队列,具有先进先出(FIFO)特点,使用了ReentrantLock来做并发控制,读写操作都上锁
private final ReentrantLock putLock = new ReentrantLock();
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
DelayDeque
DelayDeque是一个无界队列,添加进DelayDeque的元素会经过compareTo方法计算,然后按照时间 进行排序,排在队头的元素是最早到期的,越往后到期时间越长,DelayDeque只能接受Delayed接口类型 如图所示,队列里的元素并不是按照先进先出的规则,而是按照过期时间
示例
package thread.customthreadpool.delayDeque; import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit; public class MyDelayed implements Delayed { private final String taskName ;
private final long nowTime = System.currentTimeMillis();
private final long expireTime ; public MyDelayed(String taskName,long expireTime) {
this.taskName = taskName;
this.expireTime = expireTime;
} @Override
public long getDelay(TimeUnit unit) {
return unit.convert((nowTime+expireTime) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
} @Override
public int compareTo(Delayed o) {
MyDelayed myDelayed = (MyDelayed) o;
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
} @Override
public String toString() {
return "MyDelayed{" +
"taskName='" + taskName + '\'' +
", nowTime=" + nowTime +
", expireTime=" + expireTime +
'}';
}
}
package thread.customthreadpool.delayDeque;
import java.util.concurrent.*;
public class MyDelayQueue {
private static final DelayQueue<MyDelayed> delayQueue = new DelayQueue<>();
private static final ExecutorService service = Executors.newCachedThreadPool();
public static void main(String[] args) throws InterruptedException {
service.submit(() -> {
delayQueue.put(new MyDelayed("A-Task",5000));
delayQueue.put(new MyDelayed("B-Task",4000));
delayQueue.put(new MyDelayed("C-Task",3000));
delayQueue.put(new MyDelayed("D-Task",2000));
delayQueue.put(new MyDelayed("E-Task",1000));
});
while (true){
System.out.println(delayQueue.take());
}
}
}
result

应用场景
1.美团外卖订单:当我们下单后没付款 ,30分钟后将自动取消订单
2.缓存,对于某些任务,需要在特定的时间清理;
and so on
LinkedTransferQueue
当消费线程从队列中取元素时,如果队列为空,那么生成一个为null的节点,消费者线程就一直等待,此时如果生产者线程发现队列中有一个null节点, 它就不入队了,而是将元素填充到这个null节点并唤醒消费者线程,然后消费者线程取走元素。
LinkedTransferQueue是 SynchronousQueue 和 LinkedBlockingQueue 的整合,性能比较高,因为没有锁操作, SynchronousQueue不能存储元素,而LinkedTransferQueue能存储元素,
PriorityBlockingQueue
PriorityBlockingQueue是一个无界的阻塞队列,同时是一个支持优先级的队列,读写操作都是基于ReentrantLock, 内部使用堆算法保证每次出队都是优先级最高的元素
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
java线程池-工作队列workQueue的更多相关文章
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java线程池使用说明
Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...
- Java线程池的几种实现 及 常见问题讲解
工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
- [转 ]-- Java线程池使用说明
Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...
- Java 线程池框架核心代码分析
前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...
- java线程池的使用与详解
java线程池的使用与详解 [转载]本文转载自两篇博文: 1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html ...
- java 线程池 并行 执行
https://github.com/donaldlee2008/JerryMultiThread/blob/master/src/com/jerry/threadpool/ThreadPoolTes ...
- Java线程池带图详解
线程池作为Java中一个重要的知识点,看了很多文章,在此以Java自带的线程池为例,记录分析一下.本文参考了Java并发编程:线程池的使用.Java线程池---addWorker方法解析.线程池.Th ...
随机推荐
- weex打包android apk采坑之旅(windows)
1. npm install weex-toolkit -g 后weex命令不起作用 ,解决办法把weex.cmd所在的目录添加到环境变量PATH 2.weex命令每次报找不到文件'C:\Progra ...
- Jmeter二次开发 java取样器示例
public SampleResult runTest(JavaSamplerContext javaSamplerContext) { //生成sampleResult对象,用于请求的命名.标记状态 ...
- Xshell 打开时,初始运行卡慢优化方法
我使用的是Xshell 6免费版,有需要的同学可以去这个地址下载:https://www.netsarang.com/download/down_form.html?code=622 一开始安装完Xs ...
- SpringCloud升级之路2020.0.x版-4.maven依赖回顾以及项目框架结构
本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford 我们先来回顾下 m ...
- Java基础——运算符(同c语言)
- i春秋CTF-web-upload
------------恢复内容开始------------ 记一道文件上传的题目. 题目告诉我们随意上传,第一想到的当然给他上传一个木马文件然后蚁剑拿shell,上传之后可以在源码里查看我们上传的文 ...
- Android开发音视频方向学习路线及资源分享,学完还怕什么互联网寒冬?
接触Android音视频这一块已经有一段时间了,跟普通的应用层开发相比,的确更花费精力.期间为了学习音视频的录制,编码,处理也看过大大小小的几十个项目.总体感觉就是知识比较零散,对刚入门的朋友比较不友 ...
- Logback 快速入门 / 使用详解
官方文档: http://logback.qos.ch/manual/index.html 一.简介 Java 开源日志框架,以继承改善 log4j 为目的而生,是 log4j 创始人 Ceki Gü ...
- Windows上的暗色调Puppet书籍翻译写作环境
翻译环境包括两个部分,写作部分和电子书,Windows上,前者用gVim,后者用SumatraPDF,二者都是绿色软件,都可以定义成暗色系风格. gVim 全屏需要使用一个叫做gvimfullscre ...
- sqli-labs lesson 38-45
从page3也就是less 38开始进入了堆叠注入(stacked injection) stacked injection: 简单来说就是进行SQL注入时注入了多条语句.因为之前我们都是只进行过注入 ...