Java多线程案例之线程池
创始人
2024-05-19 23:45:57
0

前言:在讲解线程池的概念之前,我们先来谈谈线程和进程,我们知道线程诞生的目的其实是因为进程太过重量了,导致系统在 销毁/创建 进程时比较低效(具体指 内存资源的申请和释放)。

而线程,其实做到了共享内存资源,新的线程复用之前的资源(也就不必再申请了)。

但是如果线程创建的速率进一步的频繁,此时线程创建销毁的开销仍然不能忽略,这时就需要线程池来进一步优化这里的速度了。

一、介绍线程池

线程池是一种线程使用模式。当线程过多时会带来调度开销,进而影响缓存局限性和整体性能。

线程池维护这多个线程,等待监督管理分配可并发执行的任务,避免了处理短时间任务时创建与销毁线程的代价,线程池不仅能保证内核的充分利用,还能防止过分调度。

为什么线程池比创建新线程快?

首先我们要明白线程池中取线程的过程,池中的线程执行任务时候,不需要再重新创建线程,而是直接从池子里取出一个现成的线程,直接使用,使用之后并非销毁,而是放回到线程池中。

回到上面的问题,那为什么直接取一个现成的线程要快一点呢?

:可能有人会回答,创建线程需要申请资源,因此利用现有的线程池比创建线程更轻量,虽然创建线程确实是有申请资源,但这并不是最主要的。

那就先说结论吧,使用线程池是纯用户态操作,而创建线程需要经历用户态到内核态的转变。因此,线程池这种纯用户态要比后者快。

用户态:每个线程都自己执行自己的逻辑。

内核态;一个系统只有这一份内核在执行逻辑,这个内核需要给所有的进程提高服务。

下面进一步讲解线程和线程池的运作过程:

创建线程:我们知道线程本质上是PCB(内核中的数据结构),当应用程序发起一个 创建线程 的行为,应用程序就需要通过系统调度,进入到操作系统内核中执行,内核完成PCB的创建之后,再把PCB加入到调度队列中,之后再返回给应用程序。

线程池:从线程池中取线程,把线程放回线程池,都在操作系统内核中实现,这是纯用户态实现的逻辑,用户态的每个进程都是自己执行自己的逻辑,运行较快。

 二、标准库中的线程池

标准库中我们一般通过工厂模式(不通过new关键字而使用工厂类来创建对象,能够让创建对象变得简单而且更方便的修改对象,属于创建型模式。)的方法来创建线程池:

//此处创建线程池,没有显示的new,而是通过另外Executors类的静态方法newCachedThreadPool来完成
//这种做法叫做工厂模式,对应此处的newCachedThreadPool()方法就是工厂方法。ExecutorService pool = Executors.newCachedThreadPool();

为何需要使用工厂模式创建线程而不使用常见的 构造方法呢?

因为构造方法常见的提供多种构造实例的方式是:重载,而重载要求 参数的个数/类型 不同。

这就带来了一些限制。

比如:

此处的两个版本的构造方法就无法形成重载。解决这个问题就是使用普通方法来代替构造方法,使普通方法在里面分别构造Point对象,再通过一些其他手段进行设置即可。

线程池单纯的使用也是非常简单,使用submit方法,把任务交给线程池即可,线程池中会有一些线程来负责完成这里的任务。

        ExecutorService pool = Executors.newCachedThreadPool();pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});

三、模拟实现线程池

我们知道一个线程池可以同时提交N个任务,对应的线程池中有M个线程来负责完成这N个任务。

可能会有人问,为什么N个任务不对应N个线程来完成任务呢?

这时因为如果这样设置的话,由于开发中很多时候任务难度是不同的,如果设置N对N关系的话,可能会出现有的线程很早就完成任务了,然后就在旁边干等着,造成 一个线程干活,多个线程围观的场面,这是很尴尬的。

如何把N个任务分配给M个线程执行呢?

利用生产者消费者模型即可,首要步骤就是实现一个阻塞队列,每个被提交的任务,都被放入阻塞队列中,让M个线程来取队列中的元素,如果队列为空,M个线程就阻塞等待,如果队列不为空,每个线程就来领取任务,执行完手头上的任务时,再去取下一个任务,直到队列为空,线程继续阻塞等待。

代码实现:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;class MyThreadPool {private BlockingQueue queue = new LinkedBlockingQueue<>();public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}public MyThreadPool(int m) {//在构造方法中,创建出M个线程,负责完成工作。for (int i = 0; i < m; i++) {Thread t = new Thread(() -> {while(true) {try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}
}
public class Demo25 {public static void main(String[] args) throws InterruptedException {MyThreadPool pool = new MyThreadPool(10);for (int i = 0; i < 1000; i++) {int taskId = i;pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行当前任务"+taskId+"当前线程"+Thread.currentThread().getName());}});}}
}

运行结果:

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...