• 为什么要用线程池?

我们都知道,每一次创建一个线程,JVM后面的工作包括:为线程建立虚拟机栈、本地方法栈、程序计数器的内存空间(下图可看出),所以线程过多容易导致内存空间溢出。同时,当频繁的创建和销毁线程容易浪费系统的计算能力在资源的回收和申请中。

另外:创建过多的线程,会导致cpu在线程中的切换时间比处理时间还多,大大降低了系统的吞吐量。因此我们使用线程池如下好处:

  1. 有效控制线程的数量,防止线程数量过多。
  2. 提高线程的利用程度,避免频繁的创建及销毁线程。
  3. 有更灵活的线程使用方式及拒绝措施。

再给大家看看阿里开发规约里面是怎么说的

  • 线程的快速示例

我知道大多数人都希望先看看线程池怎么创建,然后再深入了解。下面给大家一个demo

         //存放任务的阻塞队列
BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(10);
//BasicThreadFactory是自己实现ThreadFactory接口而来
BasicThreadFactory factory = new BasicThreadFactory();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 10, 60,
TimeUnit.SECONDS, queue, factory,
(Runnable r, ThreadPoolExecutor executor)->{
System.out.println(executor.getQueue().size()+"消息队列已满");
System.out.println("拒绝服务"); });

ThreadPoolDemo

  • 线程池相关概念

  1. 核心线程:若线程池中的线程标记为核心线程,即使核心线程没有运行任务,它也不会被销毁,会一直存在于线程池中,直至线程池被shutdown。
  2. 非核心线程:当线程池中没有空闲的核心线程时,线程池会创建一个非核心线程,并且非核心线程的一定时间内处于空闲状态的时候,非核心线程会被销毁。
  3. 阻塞队列:阻塞队列是当线程池中的没有能用于处理任务的线程时,会把该任务放入阻塞队列,待有能用于处理的线程时,把任务从队列取出处理,阻塞队列的长度可以设置。
  4. 拒绝服务处理:当线程池中的没有线程能提供处理,并且阻塞队列的空间已满,此时会触发拒绝服务异常,开发人员可以根据自己的需求定制不同的处理策略。
  • 创建线程池的7个参数

一般我们推荐使用ThreadPoolExecutor()自定义创建线程池,因为这比较灵活切可控。

  1. int corePoolSize  核心线程数,即确定有多少个核心线程。
  2. int maximumPoolSize  最大线程数,即限定线程池中的最大线程数量。
  3. long keepAliveTime  非核心线程的存活时间,配合下面的TimeUnit参数确定时间。
  4. TimeUnit unit  一个时间类型的枚举类。有从纳秒到天的时间量度,配合上面的keepAliveTime确定非核心线程的存活时间。
  5. BlockingQueue<Runnable> workQueue   装载Runnable的阻塞队列,具体类型可以自己确定。
  6. ThreadFactory threadFactory  线程工厂,这是一个函数式接口,里面只定义了一个newThread(Runnable task)方法,需要自己实现工厂的方法,在这里我们可以对线程进行自定义的初始化,例如给线程设定名字,这样方便后期的调试。
  7. RejectedExecutionHandler handler   拒绝服务处理,这也是一个函数式接口,我们需要实现rejectedExecution(Runnable r, ThreadPoolExecutor executor)这个方法,这里可以根据需求自定义你希望在处理逻辑。当然Java里面也有已经定义好的四种策略静态类。可以通过ThreadPoolExecutor调用
  • Executors中实现的线程池类型

下面介绍的线程池类型,是Jdk帮我们制定好的策略。但是,有的线程池类型中,要么存在线程数量无限制、要么存在阻塞队列长度无限制,但是这些应该在开发中避免,因为一旦并发过高,会导致大量的对象积压,导致JVM内存溢出。

写在前面:jdk提供了默认的工厂方法和默认的默认的拒绝处理策略。

默认拒绝策略是:不执行并抛出异常

默认的工厂方法是:对线程进行安全检查并命名。

     static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix; DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
} public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}

defaultFactory

  1. FixedThreadPool       固定核心线程的线程池。

特点:它的核心线程数量就是最大线程数,所以线程池内的线程永远不会消亡,它采用了无参数的链表阻塞队列,最大的任务数可达232-1个。因此存在任务积压导致内存溢出的风险。

   2.  CachedThreadPool   缓存线程池

特点:没有核心线程,线程池不能满足任务运行时会创建新的线程,线程数量没有上限。默认的消亡时间为60秒。值得注意的是:它的阻塞队列是SynchronousQueue,这是一个没有存储性质的阻塞队列,它的取值操作和放入操作必须是互斥的。根据源码文档的解释,可以理解为每当有任务放入时会立即有线程将它取出执行。

  3.  ScheduledThreadPool  固定调度线程池

特点:有固定的核心线程,线程的数量没有限制,默认存活时间为60秒。同时支持定时及周期性任务执行。

  4. SingleThreadExecutor  单核心线程池

特点:只有一个核心线程,所以能保证任务的串行化执行。

  5. WorkStealingPool  并行执行线程池

特点:在jdk8中实现 线程池。它内部的线程池实现是ForkJoinPool,这是一个可以同时利用多个线程来执行任务的线程池。无参默认使用CPU数量的线程数执行任务,由于这个线程池比较复杂,下次专门写一篇博文用于更新。

  • 线程池的调用流程

需要注意的是:线程池设计的流程是先利用核心线程处理、核心线程不能处理即把它放入阻塞队列,最好才创建线程来执行任务,直到新建线程也失败才调用拒绝服务处理。

试着理解一下这样设计的好处。可以看到,创建线程永远不是最先想到的办法,线程池尽量避免创建线程。因为创建线程需要调用全局锁来确定线程的正确创建,同时也因为线程创建和销毁也需要消耗资源,所以这种方式在最大努力的避免这种情况的发生。

  • 线程池的关闭

虽然在实际的开发中,线程池一般是随着项目的部署一起存活的,不会经常关闭,但是还是需要了解如何关闭,怎么关闭比较安全。

线程池可通过调用线程池的shutdownshutdownNow方法来关闭线程池.
它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止.
但是它们存在一定的区别

  • shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
  • shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程.

只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true.
当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true.
至于应该调用哪一种方法,应该由提交到线程池的任务的特性决定,通常调用shutdown方法来关闭线程池,若任务不一定要执行完,则可以调用shutdownNow方法.

线程关闭的方法转载于作者:全网搜索关注JavaEdge
链接:https://www.nowcoder.com/discuss/152050?type=0&order=0&pos=6&page=0

JAVA线程池的创建与使用的更多相关文章

  1. Java线程池的创建详解

    本篇文章主要总结了Java创建线程池的三种方式以及线程池参数的详细说明,对线程池感兴趣的同学可以作为参考学习. 1)通过工具类java.util.concurrent.Executors的静态方法来创 ...

  2. java线程池及创建多少线程合适

    java线程池 1.以下是ThreadPoolExecutor参数完备构造方法: public ThreadPoolExecutor(int corePoolSize,int maximumPoolS ...

  3. java线程池的创建使用

    利用java的多线程编程可以大大的提高系统的并发运行效率,线程越多并发执行的任务就越多,但是并不意味着效率会一直提高,相反会得到适得其反的效果. java中的多线程编程一共有三种方法: 继承Threa ...

  4. java 线程池的创建方式

    package com.nf147.Constroller; import java.util.concurrent.ExecutorService; import java.util.concurr ...

  5. JAVA线程池的创建

    /** * 创建不同类型的线程池 Executors * * @author */ public class ThreadPoolTest01 { public static void main(St ...

  6. Java线程池的那些事

    熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...

  7. 使用Java 线程池的利弊及JDK自带六种创建线程池的方法

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  8. Java线程池解析

    Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...

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

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

随机推荐

  1. 第08组 Beta冲刺(3/5)

    队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述 ? 维持进度,检查需求 展示GitHub当日代码/文档签入记录 接下来的 ...

  2. 去除批次效应 sva

    Surrogate Variable Analysis http://www.bioconductor.org/packages/release/bioc/html/sva.html

  3. elasticsearch容量规划

    https://docs.bonsai.io/article/123-capacity-planning Capacity Planning Capacity planning is the proc ...

  4. PS 个人常用功能

    PS是什么? Adobe Photoshop,简称"PS",是由Adobe Systems开发和发行的图像处理软件. 不是美工,为什么要学PS? 1)写博客时,有些需要的素材图片有 ...

  5. php扩展模块 opcache安装教程

    php扩展模块 opcache安装教程PHP5.5.0以后版本自带Opcache加速器,但默认情况下木有启用.所以编译PHP的时候 我们想要启用该PHP加速器就应该添加参数 : –enable-opc ...

  6. SpringApplication常见用法说明

    启动方式 方式1:在main方法中执行SpringApplication.run()这种方式来启动我们的工程 // 方式一 @SpringBootApplication public class Ap ...

  7. 单口 RAM、伪双口 RAM、真双口 RAM、单口 ROM、双口 ROM 到底有什么区别呢?

    打开 IP Catalog,搜索 Block Memory Generator,即可看到其 Memory Type 可分为 5 中,分别是单口 RAM(Single Port RAM).伪双口 RAM ...

  8. MySQL面试题及答案整理,史上最全!

    原文链接:https://juejin.im/post/5d351303f265da1bd30596f9 前言 本文主要受众为开发人员,所以不涉及到MySQL的服务部署等操作,且内容较多,大家准备好耐 ...

  9. java中System.err.print和System.out.print区别

    System.err.print    是报错专用输输出,有颜色标记,所有err打印的都在顶行输出 System.out.print   是标准输出,白底黑字 package iobuffer; pu ...

  10. 推荐一个GOLANG入门很好的网址

    推荐一个GOLANG入门很好的网址,栗子很全 https://books.studygolang.com/gobyexample/