原文地址:https://www.jianshu.com/p/ec5b8cccd87d

java和spring都提供了线程池的框架

java提供的是Executors;

spring提供的是ThreadPoolTaskExecutor;

一、基本使用

Executors提供了4个线程池,

  1. FixedThreadPool
  2. SingleThreadExecutor
  3. CachedThreadPool
  4. ScheduledThreadPool

FixedThreadPool-固定线程池

public class FixPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i =0;i<10;i++){
executorService.execute(()->{
Thread wt = Thread.currentThread(); String format = String.format("当前线程名称:%s,当前时间:%s", wt.getName(), LocalDateTime.now());
System.out.println(format);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}

效果如下:

可以看到5个线程在接任务,接完5个任务之后就停止了1秒,完成之后继续接任务;

SingleThreadExecutor-单一线程池,线程数为1的固定线程池

为什么要创造这么一个线程池出来呢?

因为有些时候需要用到线程池的队列任务机制,又不想多线程并发。此时就需要用单一线程池了。

以下两种写法完全一样的效果

ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(1);

CachedThreadPool-缓存线程池,线程数为无穷大的一个线程池

当有空闲线程的时候就让空闲线程去做任务;

当没空闲线程的时候就新建一个线程去任务;

public class CashPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i =0;;i++){
executorService.execute(()->{
Thread wt = Thread.currentThread(); String format = String.format("缓存线程池,当前线程名称:%s,当前时间:%s", wt.getName(), LocalDateTime.now());
System.out.println(format);
try {
double d = Math.random();
int sleep = (int)(d*5000);
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread.sleep(100);
}
}
}

效果如下:



由于任务耗时不确定,所以线程池会动态根据情况去判断是否创建新的线程;

ScheduledThreadPool-调度线程池,线程会根据一定的时间规律去消化任务

分别有3个

  1. schedule(固定延时才执行任务)
  2. scheduleAtFixedRate(一定的间隔执行一次任务,执行时长不影响间隔时间)
  3. scheduleWithFixedDelay(一定的间隔执行一次任务,从任务执行接触才开始计算,执行时长影响间隔时间)
public class ScheduledPool {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
String begin = String.format("调度线程池-begin,当前时间:%s", LocalDateTime.now());
System.out.println(begin); // schedule(pool);
scheduleAtFixedRate(pool);
// scheduleWithFixedDelay(pool);
} private static void scheduleAtFixedRate(ScheduledExecutorService pool){
pool.scheduleAtFixedRate(()->{
Thread wt = Thread.currentThread();
String format = String.format("scheduleAtFixedRate-调度线程池,当前线程名称:%s,当前时间:%s", wt.getName(), LocalDateTime.now());
System.out.println(format);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
},1,1, TimeUnit.SECONDS);
} private static void scheduleWithFixedDelay(ScheduledExecutorService pool){
pool.scheduleWithFixedDelay(()->{
Thread wt = Thread.currentThread();
String format = String.format("scheduleWithFixedDelay-调度线程池,当前线程名称:%s,当前时间:%s", wt.getName(), LocalDateTime.now());
System.out.println(format);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
},1,1, TimeUnit.SECONDS);
}
private static void schedule(ScheduledExecutorService pool){
for (int i =0;i<10;i++){
pool.schedule(()->{
Thread wt = Thread.currentThread();
String format = String.format("调度线程池,当前线程名称:%s,当前时间:%s", wt.getName(), LocalDateTime.now());
System.out.println(format);
},5, TimeUnit.SECONDS);
}
pool.shutdown();
}
}

二、原理讲解

上面介绍的4个线程池工具,都是基于一个类ThreadPoolExecutor

ThreadPoolExecutor有几个重要的参数

  1. corePoolSize,核心线程数
  2. maximumPoolSize,最大线程数
  3. keepAliveTime,线程空闲时间,和4组合使用
  4. unit
  5. workQueue,工作队列,是各个工具的机制策略的核心
  6. threadFactory ,生成线程的工厂,可以代理run方法,还有给thread命名
  7. handler,拒绝策略,当任务太多的时候会执行的方法,框架有4个实现好的策略,可以自己写自己的策略。

对于fixPool线程池,corePoolSize=maximumPoolSize=n,keepAliveTime=0,workQueue=LinkedBlockingQueue,threadFactory和handler都是默认的。

对于cashPool线程池,corePoolSize=0,maximumPoolSize=2^32,keepAliveTime=60s,workQueue=SynchronousQueue,threadFactory和handler都是默认的。

我们先看一看execute方法

其中ctl参数是一个核心参数,保存着线程池的运行状态和线程数量,通过workerCountOf()获取当前的工作线程数量。

execute整个过程分成3个部分。

  1. 当工作线程数少于核心线程数,创建一个工作线程去消化任务。
  2. 当线程池在运行状态而且能把任务放到队列,则接受任务,调用addWorker让线程去消化队列的任务。
  3. 让线程获取消化任务失败,拒绝任务。

核心一 workQueue.offer

对于fixPool,由于workQueue是LinkedBlockingQueue,所以offer方法基本会返回true。

对于cashpool,workQueue是SynchronousQueue,如果没有消费者在take,则会立马返回false,然后立马新建一个线程。

核心二 getTask

每个线程都被包装成worker对象,worker对象会执行自己的runWorker方法,方法在死循环不停得调用getTask方法去消化任务。



getTask里面最核心的是take和poll方法,这个是跟你传入的队列特性有关。

对于spring提供的ThreadPoolTaskExecutor,其实也是对ThreadPoolExecutor的一个封装。

具体看initializeExecutor方法



在执行execute方法的时候,也是执行ThreadPoolExecutor的execute方法。

结论,线程池的核心是ThreadPoolExecutor类,根据传入的workQueue的offer、poll、take方法特性的不同而诞生缓存线程池,固定线程池,调度线程等各种线程策略。

github地址:https://github.com/hd-eujian/threadpool.git

码云地址: https://gitee.com/guoeryyj/threadpool.git

线程池基本使用和ThreadPoolExecutor核心原理讲解的更多相关文章

  1. Java线程池的使用方式,核心运行原理、以及注意事项

    为什么需要线程池 java中为了提高并发度,可以使用多线程共同执行,但是如果有大量线程短时间之内被创建和销毁,会占用大量的系统时间,影响系统效率. 为了解决上面的问题,java中引入了线程池,可以使创 ...

  2. 并发编程系列:Java线程池的使用方式,核心运行原理、以及注意事项

    并发编程系列: 高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 线程池的缘由 java中为了提高并发度,可以使用多线程共同执行,但是如果有大量线程短时间之内被创建和销毁,会占用大量的 ...

  3. JDK ThreadPoolExecutor核心原理与实践

    一.内容概括 本文内容主要围绕JDK中的ThreadPoolExecutor展开,首先描述了ThreadPoolExecutor的构造流程以及内部状态管理的机理,随后用大量篇幅深入源码探究了Threa ...

  4. 手写线程池,对照学习ThreadPoolExecutor线程池实现原理!

    作者:小傅哥 博客:https://bugstack.cn Github:https://github.com/fuzhengwei/CodeGuide/wiki 沉淀.分享.成长,让自己和他人都能有 ...

  5. Java线程池使用和分析(二) - execute()原理

    相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...

  6. 线程池技术之:ThreadPoolExecutor 源码解析

    java中的所说的线程池,一般都是围绕着 ThreadPoolExecutor 来展开的.其他的实现基本都是基于它,或者模仿它的.所以只要理解 ThreadPoolExecutor, 就相当于完全理解 ...

  7. Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析

    Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...

  8. 线程池的使用及ThreadPoolExecutor的分析(一)

    说明:本作者是文章的原创作者,转载请注明出处:本文地址:http://www.cnblogs.com/qm-article/p/7821602.html 一.线程池的介绍 在开发中,频繁的创建和销毁一 ...

  9. Executor框架(三)线程池详细介绍与ThreadPoolExecutor

    本文将介绍线程池的设计细节,这些细节与 ThreadPoolExecutor类的参数一一对应,所以,将直接通过此类介绍线程池. ThreadPoolExecutor类 简介   java.uitl.c ...

随机推荐

  1. 在Windows7系统中设置虚拟内存大小

    当我们的电脑物理内存空间不够用时,操作系统就会自动从硬盘空间上分出一块空间来当内存使用,这就是虚拟内存.可以说虚拟内存是物理内存的补充,是备用的物理内存.一般来说,如果电脑里的程序不多,占用内存资源不 ...

  2. map的自动扩容与手动缩容

    map的自动扩容与手动缩容 首先还是提出问题:扩容和缩容有什么用?为什么需要扩容和缩容? 在想解答这个问题之前,首先还是需要了解一下go语言中的map go语言中的map与Java中的map实现还是有 ...

  3. 【git】关联本地仓库与远程仓库

    1.在远程建立一个空项目[项目名称]2.git init3.git remote add origin [git 地址]4.git pull origin master5.git push origi ...

  4. Java安全之Javassist动态编程

    Java安全之Javassist动态编程 0x00 前言 在调试CC2链前先来填补知识盲区,先来了解一下Javassist具体的作用.在CC2链会用到Javassist以及PriorityQueue来 ...

  5. 飞翔---------双重线性dp

    题目: 鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧.于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中. 这些鹰的起始点被设在一个N*M矩阵的左下 ...

  6. IE下文件上传, SCRIPT5: 拒绝访问 问题

    最近遇到一个比较奇葩的问题,某些ie浏览器在页面中上传文件时,无法上传.查看控制台报错: SCRIPT5: 拒绝访问. jquery-3.2.1.min.js, 行4 字符5725 .并且我的最新版I ...

  7. VMware虚拟机中共享文件夹 开机启动

    输入命令: sudo /usr/bin/vmhgfs-fuse .host:/ /mnt/hgfs -o allow_other -o uid=1000 -o gid=1000 -o umask=02 ...

  8. docker19.03使用数据卷

    一,查看当前的docker版本 [root@localhost data]# docker --version Docker version 19.03.8, build afacb8b 说明:刘宏缔 ...

  9. 开源 Open Source

    FREE 开源不等于免费 代表自由 开源 Open Source软件和源代码提供给所有人,自由分发软件和源代码能够修改和创建衍生作品软件分类:商业   收费使用  代码不公开共享  免费用 代码不公开 ...

  10. postgresql使用规范解读

    表设计规范1.建议能使用小字节数类型,就不要用大字节数类型2.建议能用varchar(N).text就不用char(N):3.建议使用default NULL,而不用default '':4.建议使用 ...