前言

前两天趁着假期在整理粉丝私信的时候看到一个粉丝朋友的私信跟我说自己现在正在复习准备面试,自己在复习到线程池这一块的时候有点卡壳,总感觉自己差了点什么。想要我帮他指导一下。这不趁着假期我也有时间我把自己这么多年的理解和从网上找的资料放在一块整理了一下都放在下面了!

1.什么是线程池

线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,我们的程序最终都是由线程进行运作。在Java中,创建和销毁线程的动作是很消耗资源的,因此就出现了所谓“池化资源”技术。线程池是池化资源技术的一个应用,所谓线程池,顾名思义就是预先按某个规定创建若干个可执行线程放入一个容器中(线程池),需要使用的时候从线程池中去取,用完之后不销毁而是放回去,从而减少了线程创建和销毁的次数,达到节约资源的目的。

2.为什么要使用线程池

2.1 降低资源消耗

前面已经讲到线程池的出现减少了线程创建和销毁的次数,每个线程都可以被重复利用,可执行多个任务。

2.2 提高系统的响应速度

每当有任务到来时,直接复用线程池中的线程,而不需要等待新线程的创建,这个动作可以带来响应速度的提升

2.3 防止过多的线程搞坏系统

可以根据系统的承受能力,调整线程池中的工作线程的数量,防止因为线程过多服务器变慢或死机。java一个线程默认占用空间为1M,可以想象一旦手动创建线程过多极有可能导致内存溢出。

3.线程池主要参数

我们可以用Executors类来创建一些常用的线程池,但是像阿里是禁止直接通过Executors类直接创建线程池的,具体的原因稍后再谈。

在了解Executors类所提供的几个线程池前,我们首先来了解一下ThreadPoolExecutor的主要参数,ThreadPoolExecutor是创建线程池的类,我们选取参数最多的构造方法来看一下:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

  

 

3.1 corePoolSize

当向线程池提交一个任务时,如果线程池中已创建的线程数量小于corePoolSIze,即便存在空闲线程,也会创建一个新线程来执行任务,直到创建的线程数大于或等于corePoolSIze。

3.2 maximumPoolSize

线程池所允许的最大线程个数,当队列满了且已经创建的线程数小于maximumPoolSize时,会创建新的线程执行任务。

3.3 keepAliveTime

当线程中的线程数大于corePoolSIze时,如果线程空闲时间大于keepAliveTime,该线程就会被销毁。

3.4 unit

keepAliveTime的时间单位

3.5 workQueue

用于保存等待执行任务的队列

3.6 threadFactory

用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n

3.7 handler

拒绝策略,当线程池和队列满了之后,再加入新线程后会执行此策略。

下面是四种线程池的拒绝策略:

AbortPolicy:中断任务并抛出异常

DiscardPolicy:中段任务但是不抛出异常

DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试提交新任务

CallerRunsPolicy:由调用线程处理该任务

4.线程池执行流程

当我们了解了ThreadPoolExecutor的七个参数后,我们就可以很快的理解线程池的流程:

 

当提交任务后,首先判断当前线程数是否超过核心线程数,如果没超过则创建新线程执行任务,否则判断工作队列是否已满,如果未满则将任务添加到队列中,否则判断线程数是否超过最大线程数,如果未超过则创建线程执行任务,否则执行拒绝策略。

5.Executors提供的线程池

executors提供了许多种线程池供用户使用,虽然很多公司禁止使用executors创建线程池,但是对于刚开始解除线程池的人来说,Executors类所提供的线程池能很好的带你进入多线程的世界。

5.1 newSingleThreadExecutor

ExecutorService executorService = Executors.newSingleThreadExecutor();

  

听名字就可以知道这是一个单线程的线程池,在这个线程池中只有一个线程在工作,相当于单线程执行所有任务,此线程可以保证所有任务的执行顺序按照提交顺序执行,看构造方法也可以看出,corePoolSize和maximumPoolSize都是1。

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}

  

5.2 newFixedThreadPool

ExecutorService executorService = Executors.newFixedThreadPool(2);

  

固定长度的线程池,线程池的长度在创建时通过变量传入。下面是newFixedThreadPool的构造方法,corePoolSize和maximumPoolSize都是传入的参数值

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}

  

5.3 newCachedThreadPool

ExecutorService executorService = Executors.newCachedThreadPool();

  

可缓存线程池,这个线程池设定keepAliveTime为60秒,并且对最大线程数量几乎不做控制。

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}

  

观察构造方法,corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制。设定keepAliveTime 为60秒,线程空闲60秒后自动结束,因为该线程池创建无限制,不会有队列等待,所以使用SynchronousQueue同步队列。

5.4 newScheduledThreadPool

创建一个定时的线程池。此线程池支持定时以及周期性执行任务的需求。下面是newScheduledThreadPool的用法:

Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"thread1");
}
});
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"thread2");
}
});
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"thread3");
}
});
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
//在1000ms后执行thread1
scheduledExecutorService.schedule(thread1,1000,TimeUnit.MILLISECONDS);
//在1000ms后每隔1000ms执行一次thread2,如果任务执行时间比间隔时间长,则延迟执行
scheduledExecutorService.scheduleAtFixedRate(thread2,1000,1000,TimeUnit.MILLISECONDS);
//和第二种方式类似,但下一次任务开始的时间为:上一次任务结束时间(而不是开始时间) + delay时间
scheduledExecutorService.scheduleWithFixedDelay(thread3,1000,1000,TimeUnit.MILLISECONDS);

  

6.为什么阿里巴巴禁止程序员用Exectors创建线程池

如果你的idea装了Alibaba Java Codeing Guidelines插件(推荐大家使用,有助于让你的代码更加规范),那么当你写了Exectors创建线程池后会看到提示:

 

并且阿里将这个用法定义为Blocker,即不允许使用,而是让人们用ThreadPoolExecutor的方式创建线程池。原因是通过ThreadPoolExecutor的方式,这样的处理方式让写的人更加明确线程池的运行规则,规避资源耗尽的风险。

 

Executors返回的线程池对象的弊端如下:

1)FixedThreadPool和SingleThreadPool:

允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

2)CachedThreadPool:

允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

下面是ThreadPoolExecutor创建线程池的简单例子

int corePoolSize=5;
int maximumPoolSize=10;
long keepAliveTime=30;
BlockingQueue blockingQueue=new ArrayBlockingQueue(2);
RejectedExecutionHandler handler=new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, blockingQueue, handler);
threadPoolExecutor.execute(thread1);

  

7.总结

那么今天这篇关于线程池的文章就到这里了,大家看完有什么不懂的欢迎在下方留言讨论,有什么不懂的也可以私信问我。另外在这里说一下私信回复的问题,因为我平时跟大家一样也要上班,私信一般是晚上会抽空看一下,一般看到大家问的问题我都会帮忙回复解决,有时间忙的时候也可能几天没时间看,这点希望大家可以见谅。

最后

欢迎关注公众号:【前程有光】,领取一套227页的2020最新的Java面试宝典

新鲜出炉!JAVA线程池精华篇深度讲解,看完你还怕面试被问到吗?的更多相关文章

  1. java 线程池第一篇 之 ThreadPoolExcutor

    一:什么是线程池? java 线程池是将大量的线程集中管理的类,包括对线程的创建,资源的管理,线程生命周期的管理.当系统中存在大量的异步任务的时候就考虑使用java线程池管理所有的线程.减少系统资源的 ...

  2. 跟着阿里p7一起学java高并发 - 第18天:玩转java线程池,这一篇就够了

    java中的线程池,这一篇就够了 java高并发系列第18篇文章. 本文主要内容 什么是线程池 线程池实现原理 线程池中常见的各种队列 自定义线程创建的工厂 常见的饱和策略 自定义饱和策略 线程池中两 ...

  3. 深入浅出Java线程池:源码篇

    前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...

  4. 捕获Java线程池执行任务抛出的异常

    捕获Java线程池执行任务抛出的异常Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常, public interface Runnable { publi ...

  5. Java线程池详解,看这篇就够了!

    构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础知识 Execu ...

  6. Java线程池的了解使用—筑基篇

    前言 Java中的线程池是一个很重要的概念,它的应用场景十分广泛,可以被广泛的用于高并发的处理场景.J.U.C提供的线程池:ThreadPoolExecutor类,可以帮助我们管理线程并方便地并行执行 ...

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

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

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

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

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

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

随机推荐

  1. python第二章:控制流

    变成实际上是一个过程的提现,每个过程都是有多个流程块组成. 比如:判断是否下雨的过程 1.布尔值 在第一章最后举例了比较操作后,最终返回的结果 True or False True 和 False是一 ...

  2. linux文本三剑客之grep

    grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正 ...

  3. 没事学学KVM(四)虚拟机基础管理

    上次学完虚机的创建.开关机.备份配置文件等,今天学学其他几个常用的虚机管理命令: 1.重命名  方法一:virsh domrename old-name new-name virsh # list I ...

  4. java之集合容器(Collection,Map)

    首先我们要了解什么是集合? 正所谓容器,比如说杯子是装水的容器,衣柜是装衣服的容器,那么集合就是装数据的容器. 集合有什么特点呢? 1.集合长度是可变的 2.集合用来存储对象 集合和数组有什么区别呢? ...

  5. deployer服务器端的配置

    虽然说是无侵入的部署工具,但是还是需要我们来做一些微小的配置的,因为出于安全考虑,我们一般不会开发 root 用户的 SSH 登录,而是使用其它用户,比如 Ubuntu 默认的 ubuntu 用户.我 ...

  6. Python使用psycopg2模块操作PostgreSQL

    https://blog.csdn.net/pcent/article/details/78643611

  7. JavaScript正则表达式随笔

    概念先行. 正则表达式:又称规则表达式.常用于检索,替换符合某种符合(规则)的文本. 组成:普通字符和元字符,普通字符就是一个普通的查找串,如abc,原字符具有特殊含义. 这里研究的是JavaScri ...

  8. Linux下如何使用X86 CPU的GPIO

    目录 1.前言 2.linux pinctrl子系统 3. pin controller driver 4.手动构造device 1.前言 在arm嵌入式开发中,各个外设具有固定的物理地址,我们可以直 ...

  9. 计算机网络-DHCP协议抓包分析总结

    前置问题:什么是(网络)协议? 网络协议为计算机网络中进行数据交换而建立的规则.标准或约定的集合. 而且: 一个网络协议至少包括三要素: 语法:用来规定信息格式;数据及控制信息的格式.编码及信号电平等 ...

  10. (1)ASP.NET Core3.1 Ocelot介绍

    1.简介 Ocelot原本设计仅为与.NET Core一起使用的,它是一个.NET API网关,作为面向使用.NET运行微型服务/面向服务的体系结构需要统一的系统入口点,即当客户端(Web站点,手机A ...