线程与线程池(一条龙详解)

一:前言

一个问题引出的学习笔记
并发类库提供的线程池实现有哪些?
其实Executors已经为我们封装好了 4 种常见的功能线程池,如下:

  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)

那么接下来就复习一波线程和线程池

二:线程

1:关于线程的理解

  • 自我理解:(这是在javaweb中的文件上传部分 实际用到的线程 来帮助理解线程)
    一个线程就是一条执行路径 ,实际例子当中 我们请求一个页面如果需要很长的时间的话,这时候我们需要设置线程来执行 请求消息中的代码,然后再写一个代码先显示出请求等待的信息
  • 官方解读:
    线程,程序执行流的最小执行单位,是行程中的实际运作单位,经常容易和进程这个概念混淆。那么,线程和进程究竟有什么区别呢?首先,进程是一个动态的过程,是一个活动的实体。简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。可以说,进程中包含了多个可以同时运行的线程。

2:线程的声明周期

线程的生命周期,线程的生命周期可以利用以下的图解来更好的理解:
在这里插入图片描述

3:单线程和多线程

在这里插入图片描述

三:线程池

1:线程池从何处而来

  • 在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在Java中,内存资源是及其宝贵的,所以,我们就提出了线程池的概念
    线程池:Java中开辟出了一种管理线程的概念,这个概念叫做线程池,从概念以及应用场景中,我们可以看出,线程池的好处,就是可以方便的管理线程,也可以减少内存的消耗。

2:线程池的好处

  • (1) 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • (2) 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • (3) 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

3:如何实现线程池

那么,我们应该如何创建一个线程池那?Java中已经提供了创建线程池的一个类:Executor类,而我们创建时,一般使用它的子类:ThreadPoolExecutor.
线程池的真正实现类是 ThreadPoolExecutor,其构造方法有如下4种:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);
}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

在这里插入图片描述

4:线程池的使用流程

// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {@Overridepublic void run() {... // 线程执行的任务}
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

5:线程池的工作原理

在这里插入图片描述

6:线程池的重要参数解读

(1):任务队列在这里插入图片描述

(2):线程工厂(threadFactory)

线程工厂指定创建线程的方式,需要实现 ThreadFactory 接口,并实现 newThread(Runnable r) 方法。该参数可以不用指定,Executors 框架已经为我们实现了一个默认的线程工厂:

(3):拒绝策略(handler)

在这里插入图片描述

7:功能性线程池

嫌上面使用线程池的方法太麻烦?其实Executors已经为我们封装好了 4 种常见的功能线程池,如下:

  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)
  • newWorkStealingPool

(1): newFixedThreadPool (固定数量的线程池)

创建的源码:

public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
}
  • 特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列。
  • 应用场景:控制线程最大并发数。
  • 使用实例:
// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){public void run() {System.out.println("执行任务啦");}
};
// 3. 向线程池提交任务
fixedThreadPool.execute(task);

(2): newWorkStealingPool

在这里插入图片描述
这个线程池的特性从名字就可以看出 Stealing,会窃取任务。
每个线程都有自己的双端队列,当自己队列的任务处理完毕之后,会去别的线程的任务队列尾部拿任务来执行,加快任务的执行速率。
至于 ForkJoin 的话,就是分而治之,把大任务分解成一个个小任务,然后分配执行之后再总和结果,

(3): newSingleThreadExecutor (单线程池)

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

特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。
应用场景:不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。
使用实例:

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

(4):newCachedThreadPool (可缓存的线程池)

创建方法的源码:

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

特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。
应用场景:执行大量、耗时少的任务。

所以它适合用在短时间内有大量短任务的场景。如果暂无可用线程,那么来个任务就会新启一个线程去执行这个任务,快速响应任务。
但是如果任务的时间很长,那存在的线程就很多,上下文切换就很频繁,切换的消耗就很明显,并且存在太多线程在内存中,也有 OOM 的风险。
使用示例:

// 1. 创建可缓存线程池对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){public void run() {System.out.println("执行任务啦");}
};
// 3. 向线程池提交任务
cachedThreadPool.execute(task);

(5): newScheduledThreadPool(定时线程池)

创建方法的源码:private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue());
}public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue(), threadFactory);
}

特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。
应用场景:执行定时或周期性的任务。
使用示例:

/ 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){public void run() {System.out.println("执行任务啦");}
};
// 3. 向线程池提交任务
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务

参考自这篇博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目升级,无缝对接 .NET 5

开启.NET5时代2020-09-14从NetCore1.1开始学起&#xff0c;然后又从2.0开始讲知识&#xff0c;再到将所有的在线项目升级并长期维护到3.1&#xff0c;转眼已经三年了&#xff0c;一直紧跟着微软的节奏有条不紊的往前走&#xff0c;我相信&#xff0c;只要是从18年末或者19年初跟…

小团队前端部署演化之路

前言 前端部署相对来说其实是一件非常容易的事情&#xff0c;无论是最原始的html页面&#xff0c;还是现在热门的三大框架&#xff0c;最后交付部署的时候&#xff0c;始终会是一些静态文件。虽然简单&#xff0c;但是对于不同的团队来说&#xff0c;都会在不同阶段有最适合他们…

GitHub 全域数字年报:携手推动开源世界的超级协作

2020年1月24日&#xff0c;Wuhan2020开源项目正式发起&#xff0c;在疫情期间累积吸引到了约3000余位技术志愿者以及近1000余位非技术志愿者在线上开展志愿行动与参与。Wuhan2020在成立后的约3个月时间内&#xff0c;通过开源协作的方式在互联网上开展志愿者支持与工作协同&…

leetcode53. 最大子数组和(暴力+贪心)

一:论语 追求利益的同时 我们需要控制度 就好比鹅厂的王者荣耀 赚的盆满钵满 坑坏了多少青少年 但是鹅厂早已经开始控制度了 二:题目 三:上码&#xff08;暴力贪心&#xff09; 1:暴力 class Solution { public:int maxSubArray(vector<int>& nums) {/**暴力解法…

Newbe.Claptrap-一套以“事件溯源”和“Actor模式”作为基本理论的服务端开发框架...

本文是关于 Newbe.Claptrap 项目主体内容的介绍&#xff0c;读者可以通过这篇文章&#xff0c;大体了解项目内容。轮子源于需求随着互联网应用的蓬勃发展&#xff0c;相关的技术理论和实现手段也在被不断创造出来。诸如 “云原生架构”、“微服务架构”、“DevOps” 等一系列关…

NCF框架揭秘直播来了!红包、抽奖、还有神秘嘉宾…(内含彩蛋)

盛派周三技术分享会直播开讲又来啦~为了更好地赋能开发者&#xff0c;盛派已将系统框架 SCF&#xff08;SenparcCoreFramework&#xff09;全部开源&#xff0c;收到了社区非常多的关注&#xff0c;现在 SCF 已正式更名为NCF&#xff08;NeuCharFramework&#xff09;&#xff…

leetcode45. 跳跃游戏 II

一:论语 己所欲 也要勿施于人 &#xff0c;每个人的经历和阅历都是不同的 你凭啥说你认为的很开心的事情 去要求别人呢 二:题目 三:上码 class Solution { public:int jump(vector<int>& nums) {/**思路:1.这里的难点就在于 我们需要判断下一步的的最远跳跃距离…

leetcode1005. K 次取反后最大化的数组和

一:论语 这个用在自己身上感觉值得反省&#xff0c;很多道理我都能明白 也能讲给别人听 但是很多时候 自己往往做的不好 而且还很容易 自我感动 最近真的很讨厌自己这样 不要自我感动 要正向积累 多去做 多去做 这只是个开始 然后慢慢的长进 再者就是坚持 二:题目 三:上码 …

.NET Core全Linux开发体验分享

“ 2016年.NET Core首个正式版本问世&#xff0c;如今已发布到了.NET Core3.1&#xff0c;再有2个月.NET5也将如约而至&#xff0c;跨平台开发已经快5年&#xff0c;然而很多人却还只是在Windows上用Visual Studio SQL Server去做.NET Core跨平台开发&#xff0c;欠缺对Linux的…

使用Microsoft Word2016无法正常对Latex文本转换的踩坑和解决方法

相信很多人都遇到像我一样的问题。word2016中&#xff0c;有latex的按钮&#xff0c;按ALT就可以开始写公式&#xff0c;复制粘贴latex公式之后&#xff0c;怎么就转换不了呢&#xff1f;就是如图这样的&#xff0c;左上角转换按钮为灰色。 上网找呀找&#xff0c;找了很多资料…

leetcode134. 加油站

一:论语 二&#xff1a;题目 三&#xff1a;上码(暴力解法超时 但方法二还是可以的) // class Solution { // public: // int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { // /** // 思路:1.暴力解法,我们遍历所…

ffmpeg 硬件解码零拷贝unity 播放

ffmpeg硬件解码问题 ffmpeg 在硬件解码&#xff0c;一般来说&#xff0c;我们解码使用cuda方式&#xff0c;当然&#xff0c;最好的方式是不要确定一定是cuda&#xff0c;客户的显卡不一定有cuda&#xff0c;windows 下&#xff0c;和linux 下要做一些适配工作&#xff0c;最麻…

Git入门教程(一)

今天开始学习Git&#xff0c;所以就把每天学的东西写下来&#xff0c;不然真的太复杂&#xff0c;容易忘记呀。 这里推荐一个网站Webscripting2 — Serverside Webscripting — xx.git&#xff0c;英文一般般的都可以上去看看&#xff0c;我觉得非常直观。 首先Git&#xff0c;…

leetcode135. 分发糖果

一:论语 二:题目 三&#xff1a;上码 class Solution { public:int candy(vector<int>& ratings) {/**思路:1.这里我们分两次遍历(从左向右 从右向左)2.当我们从左向右遍历的时候 如果右边的评分比左边孩子分数高 那么右边孩子的糖果数上就在左边孩子糖果数上加一3.当…

Java国家/地区使用限制条款引发争议

喜欢就关注我们吧&#xff01;今天 JDK/Java 15 发布&#xff08;看今天推送的头条&#xff09;&#xff0c;在 RI 包里有开发者发现其标注了一则国际使用限制条款&#xff1a;由于某些国家/地区的知识产权保护和执法有限&#xff0c;因此 JDK 源代码只能分发到授权的国家/地区…

反射的基本知识(详解)

一:反射的引出 1:问题 比如我们给出一个student类 其方法show(),我们将其写入配置文件中&#xff1b;现在我们来一个新的需求说是要改变重写一个show()方法 show()2,那么如何在不修改源码的情况下进行修改&#xff0c;这时我们通过反射就可以完成 2:过程 student类&#xf…

.NET 5.0 RC1 发布,离正式版发布仅剩两个版本,与 netty 相比更具竞争力

原文&#xff1a;http://dwz.win/Qf8作者&#xff1a;Richard翻译&#xff1a;精致码农-王亮说明&#xff1a;1. 本译文并不是完全逐句翻译的&#xff0c;存在部分语句我实在不知道如何翻译或组织就根据个人理解用自己的话表述了。2. 本文有不少超链接&#xff0c;由于微信公众…

一个例子带你搞懂python作用域中的global、nonlocal和local

在编程中&#xff0c;只要接触过函数的&#xff0c;我相信都理解什么是全局变量和局部变量&#xff0c;概念比较简单&#xff0c;这里就不做解释了。在python中&#xff0c;用global语句就能将变量定义为全局变量&#xff0c;但是最近又发现有个nonlocal&#xff0c;一时搞不太…

初识ABP vNext(10):ABP设置管理

点击上方蓝字"小黑在哪里"关注我吧定义设置使用设置前言上一篇介绍了ABP模块化开发的基本步骤&#xff0c;完成了一个简单的文件上传功能。通常的模块都有一些自己的配置信息&#xff0c;比如上篇讲到的FileOptions类&#xff0c;其中配置了文件的上传目录&#xff0…

类加载机制(整个过程详解)

一:背景 类加载机制是在我们的真个java的运行阶段中的其中一个阶段。 二:什么是快乐星球(类加载机制) 我们编写的 Java 文件都是以.java 为后缀的文件&#xff0c;编译器会将我们编写的.java 的文件编译成.class 文件&#xff0c;简单来说类加载机制就是jvm从文件系统将一系…