JAVA线程池的创建与使用
为什么要用线程池?
我们都知道,每一次创建一个线程,JVM后面的工作包括:为线程建立虚拟机栈、本地方法栈、程序计数器的内存空间(下图可看出),所以线程过多容易导致内存空间溢出。同时,当频繁的创建和销毁线程容易浪费系统的计算能力在资源的回收和申请中。
另外:创建过多的线程,会导致cpu在线程中的切换时间比处理时间还多,大大降低了系统的吞吐量。因此我们使用线程池如下好处:
- 有效控制线程的数量,防止线程数量过多。
- 提高线程的利用程度,避免频繁的创建及销毁线程。
- 有更灵活的线程使用方式及拒绝措施。
再给大家看看阿里开发规约里面是怎么说的
线程的快速示例
我知道大多数人都希望先看看线程池怎么创建,然后再深入了解。下面给大家一个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
线程池相关概念
- 核心线程:若线程池中的线程标记为核心线程,即使核心线程没有运行任务,它也不会被销毁,会一直存在于线程池中,直至线程池被shutdown。
- 非核心线程:当线程池中没有空闲的核心线程时,线程池会创建一个非核心线程,并且非核心线程的一定时间内处于空闲状态的时候,非核心线程会被销毁。
- 阻塞队列:阻塞队列是当线程池中的没有能用于处理任务的线程时,会把该任务放入阻塞队列,待有能用于处理的线程时,把任务从队列取出处理,阻塞队列的长度可以设置。
- 拒绝服务处理:当线程池中的没有线程能提供处理,并且阻塞队列的空间已满,此时会触发拒绝服务异常,开发人员可以根据自己的需求定制不同的处理策略。
创建线程池的7个参数
一般我们推荐使用ThreadPoolExecutor()自定义创建线程池,因为这比较灵活切可控。
int corePoolSize 核心线程数,即确定有多少个核心线程。
int maximumPoolSize 最大线程数,即限定线程池中的最大线程数量。
long keepAliveTime 非核心线程的存活时间,配合下面的TimeUnit参数确定时间。
TimeUnit unit 一个时间类型的枚举类。有从纳秒到天的时间量度,配合上面的keepAliveTime确定非核心线程的存活时间。
BlockingQueue<Runnable> workQueue 装载Runnable的阻塞队列,具体类型可以自己确定。
ThreadFactory threadFactory 线程工厂,这是一个函数式接口,里面只定义了一个newThread(Runnable task)方法,需要自己实现工厂的方法,在这里我们可以对线程进行自定义的初始化,例如给线程设定名字,这样方便后期的调试。
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
- FixedThreadPool 固定核心线程的线程池。
特点:它的核心线程数量就是最大线程数,所以线程池内的线程永远不会消亡,它采用了无参数的链表阻塞队列,最大的任务数可达232-1个。因此存在任务积压导致内存溢出的风险。
2. CachedThreadPool 缓存线程池
特点:没有核心线程,线程池不能满足任务运行时会创建新的线程,线程数量没有上限。默认的消亡时间为60秒。值得注意的是:它的阻塞队列是SynchronousQueue,这是一个没有存储性质的阻塞队列,它的取值操作和放入操作必须是互斥的。根据源码文档的解释,可以理解为每当有任务放入时会立即有线程将它取出执行。
3. ScheduledThreadPool 固定调度线程池
特点:有固定的核心线程,线程的数量没有限制,默认存活时间为60秒。同时支持定时及周期性任务执行。
4. SingleThreadExecutor 单核心线程池
特点:只有一个核心线程,所以能保证任务的串行化执行。
5. WorkStealingPool 并行执行线程池
特点:在jdk8中实现 线程池。它内部的线程池实现是ForkJoinPool,这是一个可以同时利用多个线程来执行任务的线程池。无参默认使用CPU数量的线程数执行任务,由于这个线程池比较复杂,下次专门写一篇博文用于更新。
线程池的调用流程
需要注意的是:线程池设计的流程是先利用核心线程处理、核心线程不能处理即把它放入阻塞队列,最好才创建线程来执行任务,直到新建线程也失败才调用拒绝服务处理。
试着理解一下这样设计的好处。可以看到,创建线程永远不是最先想到的办法,线程池尽量避免创建线程。因为创建线程需要调用全局锁来确定线程的正确创建,同时也因为线程创建和销毁也需要消耗资源,所以这种方式在最大努力的避免这种情况的发生。
线程池的关闭
虽然在实际的开发中,线程池一般是随着项目的部署一起存活的,不会经常关闭,但是还是需要了解如何关闭,怎么关闭比较安全。
线程池可通过调用线程池的shutdown或shutdownNow方法来关闭线程池.
它们的原理是遍历线程池中的工作线程,然后逐个调用线程的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线程池的创建与使用的更多相关文章
- Java线程池的创建详解
本篇文章主要总结了Java创建线程池的三种方式以及线程池参数的详细说明,对线程池感兴趣的同学可以作为参考学习. 1)通过工具类java.util.concurrent.Executors的静态方法来创 ...
- java线程池及创建多少线程合适
java线程池 1.以下是ThreadPoolExecutor参数完备构造方法: public ThreadPoolExecutor(int corePoolSize,int maximumPoolS ...
- java线程池的创建使用
利用java的多线程编程可以大大的提高系统的并发运行效率,线程越多并发执行的任务就越多,但是并不意味着效率会一直提高,相反会得到适得其反的效果. java中的多线程编程一共有三种方法: 继承Threa ...
- java 线程池的创建方式
package com.nf147.Constroller; import java.util.concurrent.ExecutorService; import java.util.concurr ...
- JAVA线程池的创建
/** * 创建不同类型的线程池 Executors * * @author */ public class ThreadPoolTest01 { public static void main(St ...
- Java线程池的那些事
熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...
- 使用Java 线程池的利弊及JDK自带六种创建线程池的方法
1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...
- Java线程池解析
Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
随机推荐
- 复旦大学2018--2019学年第二学期高等代数II期末考试情况分析
一.期末考试成绩班级前十名 丁思成(99).周烁星(97).王捷翔(96).顾文颢(92).顾天翊(90).封清(89).张思哲(89).李哲蔚(88).陈钦品(88).邹年轶(88).王祝斌(88) ...
- python client.py
vi /Library/Frameworks/Python.framework/Versions//http/client.py vi /Library/Frameworks/Python.frame ...
- ubuntu容器化开发系统配置 相关下载地址(laravel)
ubuntu64位 18.04(开发使用桌面版本) Nginx PHP7.3 开发工具:PHPStorm 其他技术栈:laravel.VUE.NodeJs https://laravel.com/ h ...
- python: 添加自定义模块路径 —— 可以使用相对路径
自定义模块时,添加模块路径: sys.path.append('..')
- java web开发入门汇总
servlet 用java语言开发动态资源网站的技术,在doGet方法中拼接显示html,在doPost方法中提交数据.类似于.net的ashx技术. servlet生成的class文件存放在tomc ...
- java基础之 数据类型 & 值传递 引用传递 & String & 四种引用类型
一.Java数据类型 分为基本数据类型与引用数据类型 基本数据类型: byte:Java中最小的数据类型,在内存中占1个字节(8 bit),取值范围-128~127,默认值0 short:短整型,2个 ...
- 【技术博客】使用PhpStorm和Xdebug实现Laravel工程的远程开发及调试
目录 使用PhpStorm和Xdebug实现Laravel工程的远程开发及调试 简介 PhpStorm中的远程开发 1. 配置服务器 2. 配置路径对应 3. 配置同步 4. 进行代码同步 5. 优点 ...
- 【2019年06月28日】A股最便宜的股票
查看更多A股最便宜的股票:androidinvest.com/CNValueTop/ 经典价值三因子选股: 市盈率PE.市净率PB 和 股息分红率,按照 1:1:1的权重,选择前10大最便宜的股票. ...
- xcode添加一个真机设备
1.首先先安装Xcode并且运行Xcode,点击左角菜单Xcode -> Preferences:点击Accounts+号弹菜单点击Add Apple ID:弹框输入账号密码普通账号行需要发者账 ...
- WebStorm ------------ 调整字体大小和背景
WebStorm 一款前端编写工具,使用方式与idea 相似 如何调整字体大小 在设置里面找 设置编码背景 拷贝一个样式,,在此样式下进行更改 开始设置 设置好后 dd