1、Executors与ThreadPoolExecutor

  • ThreadPoolExecutor

    • 可以灵活的自定义的创建线程池,可定制性很高
    • 想创建好一个合适的线程池比较难
    • 使用稍微麻烦一些
    • 实际中很少使用
  • Executors
    • 可以创建4种线程池,这四种线程池基本上已经包含了所有需求,将来根据业务特点选用就好
    • 使用非常简单
    • 实际中很常用

使用方法:

package com.collection.test;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors; public class ThreadPoolExecutorTest {
//private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
//private static Executor executor = Executors.newFixedThreadPool(5);
//private static Executor executor = Executors.newSingleThreadExecutor();
//private static Executor executor = Executors.newCachedThreadPool();
private static Executor executor = Executors.newScheduledThreadPool(5); public void executeTask(){
Task1 task1 = new Task1();//构建任务1
Task2 task2 = new Task2();//构建任务2
executor.execute(task1);//执行任务1
executor.execute(task2);//执行任务2
} /*
* 基本任务2
*/
class Task1 implements Runnable{
public void run() {
//具体任务的业务
for(int i=0;i<1000;i++){
System.out.println("hello xxx!!!");
}
}
} /*
* 基本任务2
*/
class Task2 implements Runnable{
public void run() {
//具体任务的业务
for(int i=0;i<5;i++){
System.out.println("hello world2!!!");
}
}
} public static void main(String[] args) {
ThreadPoolExecutorTest test = new ThreadPoolExecutorTest();
test.executeTask();
}
}

2、Executors可以创建的几种线程池简介

  • newFixedThreadPool(int corePoolSize)

    • 创建一个线程数固定(corePoolSize==maximumPoolSize)的线程池
    • 核心线程会一直运行
    • 如果一个核心线程由于异常跪了,会新创建一个线程
    • 无界队列LinkedBlockingQueue
  • newSingleThreadExecutor
    • 创建一个线程数固定(corePoolSize==maximumPoolSize==1)的线程池
    • 核心线程会一直运行
    • 无界队列LinkedBlockingQueue
    • 所有task都是串行执行的(即同一时刻只有一个任务在执行)
  • newCachedThreadPool
    • corePoolSize==0
    • maximumPoolSize==Integer.MAX_VALUE
    • 队列:SynchronousQueue
    • 创建一个线程池:当池中的线程都处于忙碌状态时,会立即新建一个线程来处理新来的任务
    • 这种池将会在执行许多耗时短的异步任务的时候提高程序的性能
    • 6秒钟内没有使用的线程将会被中止,并且从线程池中移除,因此几乎不必担心耗费资源
  • newScheduledThreadPool(int corePoolSize)
    • 用于执行定时或延迟执行的任务,最典型的:异步操作时的超时回调

注意:对于定时任务的执行,在实际使用中,会去使用spring定时器,非常方便

3、newFixedThreadPool(int corePoolSize)

源代码:

/**
* 1、创建一个线程数固定(corePoolSize==maximumPoolSize)的线程池,
* 2、核心线程会一直运行
* 3、无界队列LinkedBlockingQueue
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
 

说明:execute()的源代码查看第十三章 ThreadPoolExecutor源码解析

4、newSingleThreadExecutor()

源代码:

/**
* 1、创建一个线程数固定(corePoolSize==maximumPoolSize==1)的线程池
* 2、核心线程会一直运行
* 3、无界队列LinkedBlockingQueue
* 注意:所有task都是串行执行的
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

说明:execute()的源代码查看第十三章 ThreadPoolExecutor源码解析

5、newCachedThreadPool()

源代码:

/**
* 1、创建一个线程池:当池中的线程都处于忙碌状态时,会立即新建一个线程来处理新来的任务
* 2、这种池将会在执行许多耗时短的异步任务的时候提高程序的性能。
* 3、6秒钟内没有使用的线程将会被中止,并且从线程池中移除,因此几乎不必担心耗费资源
* 4、队列:SynchronousQueue
* 5、maximumPoolSize为Integer.MAX_VALUE
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
 

说明:execute()的源代码查看第十三章 ThreadPoolExecutor源码解析

6、newScheduledThreadPool(int corePoolSize)

源代码:

Executors:newScheduledThreadPool(int corePoolSize)

/**
* 创建一个线程池:该线程池可以用于执行延时任务或者定时任务
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}

ScheduledThreadPoolExecutor:ScheduledThreadPoolExecutor(int corePoolSize)

/**
* 创建一个线程池:
* corePoolSize==我们指定
* maximumPoolSize==Integer.MAX_VALUE
* keepAliveTime==0纳秒(即不回收闲置线程)
* 队列: DelayedWorkQueue
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}

说明:ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,其中调用的super构造器就是ThreadPoolExecutor的构造器。

ScheduledThreadPoolExecutor:execute(Runnable command)

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
schedule(command, 0, TimeUnit.NANOSECONDS);
}

ScheduledThreadPoolExecutor:schedule(Runnable command, long delay, TimeUnit unit)

/**
* 这个方法:其实就是将task封装一下,然后加入到DelayedWorkQueue中
* 1、DelayedWorkQueue其实就是一个DelayQueue
* 2、当有新的task加入时,DelayQueue会将其加入内部的数组对象中,并对其进行排序,在这里,排序的规则就是执行的时间,执行时间越近的排在越前
* 3、线程池中的线程在执行task时,获取最近要执行的task,然后唤醒所有等待available条件的线程来执行该任务
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit))); delayedExecute(t);
return t;
}

注意:这里的注释就是整个ScheduledThreadPoolExecutor的执行机理。

下面说一下其中调用到的一些方法。

第一部分:封装ScheduledFutureTask任务

ScheduledThreadPoolExecutor:triggerTime(long delay, TimeUnit unit)

/**
* 返回一个delayed action(延时任务)的触发时间
*/
private long triggerTime(long delay, TimeUnit unit) {
return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
} /**
* Returns the trigger time of a delayed action.
*/
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
 

说明:用于计算延时任务的触发时间。

注意:在上边的execute()方法中传递的delay是0,根据上边的代码,计算出触发时间就是now()。

ScheduledThreadPoolExecutor:内部类ScheduledFutureTask

private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> { private final long sequenceNumber;//用于打破FIFO关系的序列号
private long time;//任务执行的触发时间
/**
* 一个用于重复执行的任务的时间段(单位:纳秒)
* 0-->不重复执行的任务
* 正值:fixed-rate执行
* 负值:fixed-delay执行
*/
private final long period; /**
* 创建一个一次性的action并且指定触发时间
*/
ScheduledFutureTask(Runnable r, V result, long ns) {
super(r, result);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}

说明:ScheduledFutureTask是FutureTask的子类,上边的构造器中的super(r, result)代码如下:

FutureTask:FutureTask(Runnable runnable, V result)

private final Sync sync;//控制FutureTask的同步器

    public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}

Executors:callable(Runnable task, T result)

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

Executors:内部类RunnableAdapter

static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();//这里是真正的task运行的地方
return result;
}
}
 

注意:这里才是task真正去运行的地方。-->task.run()

至此,ScheduledFutureTask任务封装完成。

第二部分:修饰任务

ScheduledThreadPoolExecutor:RunnableScheduledFuture

protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable,
RunnableScheduledFuture<V> task) {
return task;
}

说明:这里其实就是直接返回了刚刚封装好的任务

第三部分:将延时任务加入阻塞队列

ScheduledThreadPoolExecutor:delayedExecute(Runnable command)

private void delayedExecute(Runnable command) {
if (isShutdown()) {//return runState != RUNNING;线程池状态不是RUNNING
reject(command);//回绝任务
return;
} if (getPoolSize() < getCorePoolSize())//当前线程池数量少于核心线程数
prestartCoreThread();//创建并启动一个核心线程 super.getQueue().add(command);//获取阻塞队列,并将command加入队列
}

说明:这样之后,之前封装好的任务就加入了延时队列DelayQueue(阻塞队列的一个子类)

DelayQueue:add(E e)

public boolean add(E e) {
return offer(e);
} public boolean offer(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E first = q.peek();//获取队列头部节点但不删除
q.offer(e);//将e放到q的尾部
//如果队列中只有e或者e的触发时间小于队头结点
if (first == null || e.compareTo(first) < 0)
available.signalAll();
return true;
} finally {
lock.unlock();
}
}

说明:在该方法中,将上边封装好的任务就加入了DelayQueue,并将该任务置于了队头,然后唤醒所有等待available条件的线程来执行该任务。

总结:

  • 四种线程池最常用的就是newCachedThreadPool和newFixedThreadPool(int corePoolSize)
  • 对于newScheduledThreadPool(int corePoolSize)使用比较少,因为在现代开发中,如果用于去开发定时任务程序的话,用spring定时器会非常简单

第十章 Executors源码解析的更多相关文章

  1. 第十四章 Executors源码解析

    前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...

  2. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  3. EventBus (三) 源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  4. Android EventBus源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  5. Java线程池源码解析

    线程池 假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率.线程池能够对线程进行统一的分 ...

  6. Java 8 ThreadLocal 源码解析

    Java 中的 ThreadLocal是线程内的局部变量, 它为每个线程保存变量的一个副本.ThreadLocal 对象可以在多个线程中共享, 但每个线程只能读写其中自己的副本. 目录: 代码示例 源 ...

  7. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  8. 第十三章 ThreadPoolExecutor源码解析

    ThreadPoolExecutor使用方式.工作机理以及参数的详细介绍,请参照<第十二章 ThreadPoolExecutor使用与工作机理 > 1.源代码主要掌握两个部分 线程池的创建 ...

  9. [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...

  10. Spark 源码解析:TaskScheduler的任务提交和task最佳位置算法

    上篇文章<  Spark 源码解析 : DAGScheduler中的DAG划分与提交 >介绍了DAGScheduler的Stage划分算法. 本文继续分析Stage被封装成TaskSet, ...

随机推荐

  1. 强行修改 User-Agent, 访问对应的端

    location /{ proxy_pass http://localhost:18080; proxy_set_header User-Agent "Mozilla/5.0 (Window ...

  2. OSG开发笔记(三十一):OSG中LOD层次细节模型介绍和使用

    前言   模型较大的时候,出现卡顿,那么使用LOD(细节层次)进行层次细节调整,可以让原本卡顿的模型变得不卡顿.  本就是LOD介绍.   Demo      LOD 概述   LOD也称为层次细节模 ...

  3. 《JVM第9课》垃圾回收器

    先来看一张图,串行代表两个垃圾回收器按顺序执行,并行代表同时执行.STW代表工作线程暂停,Stop The World的意思. 垃圾回收器 执行顺序 执行方式 作用区域 使用算法 说明 Serial ...

  4. downloadFile:base64数据导出文件,文件下载

    function downloadFile(filename, data){ let DownloadLink = document.createElement('a'); if ( Download ...

  5. 4G模组AT指令 | MQTT应用指南!

    今天,老师傅讲的是关于4G模组AT指令之MQTT应用,以4G模组Air780E为例: 一.MQTT 协议简介 1.1 MQTT 概述 MQTT 是一种轻量级的消息传输协议,旨在在物联网(IoT)应用中 ...

  6. Moebius for SQL Server

    Moebius(莫比斯)介绍 Moebius数据库多活集群是格瑞趋势为SQL Server数据库研发的能够同时满足可用性.数据安全.容灾.读写分离.负载均衡的一站式多活集群.集群的名字取自Moebiu ...

  7. 利用 Java 实现组合式解析器

    Ward Cunningham 曾经说过,干净的代码清晰地表达了代码编写者所想要表达的东西,而优美的代码则更进一步,优美的代码看起来就像是专门为了要解决的问题而存在的.在本文中,我们将展示一个组合式解 ...

  8. Tornado框架之基础(一)

    知识点 了解什么是Tornado框架 了解Tornado与Django的区别 Tornado的安装 了解Tornado的原理 掌握Tornado的基本写法 掌握Tornado的基本模块 tornado ...

  9. Electron(2) - 下载与解压缩

    1.下载文件 主线程中调用下载 win.webContents.downloadURL(url) 监听下载事件 //监听下载动作 win.webContents.session.on('will-do ...

  10. datagridview点击列头对当前列进行排序的功能无效

    DataGridView 的默认行为是支持通过单击列头对列进行排序,但在以下情况下可能会取消该功能或无法使用: 1. 绑定的数据源不支持排序 如果 DataGridView 的数据源是绑定到一个不支持 ...