目录
2.2.2.newSingleThreadExector
2.2.3.newCachedThreadPool
首页 Java Java基础 Java讲解ThreadPool线程池

Java讲解ThreadPool线程池

Mar 03, 2021 am 10:36 AM
java

Java讲解ThreadPool线程池

ThreadPool线程池

  • 1.线程池的优势
    • 1.1.引言
    • 1.2.为什么要使用线程池
  • 2.线程池的使用
    • 2.1.架构说明
    • 2.2.线程池的三大方法
      • 2.2.1.newFixedThreadPool(int)方法
      • 2.2.2.newSingleThreadExector
      • 2.2.3.newCachedThreadPool
  • 3.ThreadPoolExecutor底层原理
  • 4.线程池7大重要参数

(相关免费学习推荐:java基础教程

1.线程池的优势

1.1.引言

与数据库线程池类似,如果没有数据库连接池,那么每次对数据库的连接池都要new来获取连接池。重复的连接和释放操作会消费大量的系统资源,我们可以使用数据库连接池,直接去池中取连接池。
同样,在没有线程池之前,我们也是通过new Thread.start()来获取线程,现在我们也不需要new了,这样就能实现复用,使得我们系统变得更加高效。

1.2.为什么要使用线程池

例子:

  • 10年前单核CPU电脑,假的多线程,像马戏团小丑玩多个球,CPU需要来回切换。
  • 现在是多核电脑,多个线程各自跑在独立的CPU上,不用切换效率高。

线程池的优势:

线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。

它的主要特点为:

  • 线程复用
  • 控制最大并发数
  • 管理线程

优点:

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

2.线程池的使用

2.1.架构说明

Executor 框架是什么?

Java Doc中是这么描述的

An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.

执行提交的Runnable任务的对象。这个接口提供了一种将任务提交与如何运行每个任务的机制,包括线程的详细信息使用、调度等。通常使用Executor而不是显式地创建线程。

Java中的线程池是通过Executor框架实现的,,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类。而我们常用的接口是ExecutorService子接口Executors是线程的工具类(类似数组的工具类Arrays,集合的工具类Collections)ThreadPoolExecutor是这些类的重点我们可以通过辅助工具类Executors拿到ThreadPoolExecutor线程池
在这里插入图片描述
各个类更加详细的介绍如下:

Executor所有线程池的接口,只有一个方法,该接口定义执行Runnable任务的方式
ExecutorService 增加Executor的行为,是Executor实现类的最直接的接口,该接口定义提供对Executor的服务
Executors 线程池工厂类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了
ScheduledExecutorService:定时调度接口。
AbstractExecutorService 执行框架抽象类。
ThreadPoolExecutor JDK中线程池的具体实现,一般用的各种线程池都是基于这个类实现的

2.2.线程池的三大方法

2.2.1.newFixedThreadPool(int)方法

Exectors.newFixedThreadPool(int) -->执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程

	public static void main(String[] args) {
		//一池5个受理线程,类似一个银行5个受理窗口。不管你现在多少个线程,都只有5个
		ExecutorService threadPool=Executors.newFixedThreadPool(5); 
		
		try {
			//模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务。
			for(int i=1;i<=10;i++){
				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
				threadPool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}

在这里插入图片描述
可以看到执行结果。池子中有5个线程,相当于5个工作人员对外提供服务,办理业务。图中1号窗口办理了两次业务,银行的受理窗口可以多次被复用。也不一定是每个人办理两次,而是谁办理的快谁就办理的多。

当我们再线程执行的过程中加400ms的延迟,可以看看效果

public static void main(String[] args) {
		//一池5个受理线程,类似一个银行5个受理窗口。不管你现在多少个线程,都只有5个
		ExecutorService threadPool=Executors.newFixedThreadPool(5); 
		
		try {
			//模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务。
			for(int i=1;i<=10;i++){
				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
				threadPool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
				try {
					TimeUnit.MILLISECONDS.sleep(400);
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}

在这里插入图片描述
此时说明网络拥堵的情况下或者办理业务比较慢,则线程池办理业务任务分配情况比较平均。

2.2.2.newSingleThreadExector

Exectors.newSingleThreadExector()–>一个任务一个任务的执行,一池一线程

public static void main(String[] args) {
	//一池一个工作线程,类似一个银行有1个受理窗口
	ExecutorService threadPool=Executors.newSingleThreadExecutor(); 	
	try {
		//模拟有10个顾客过来银行办理业务
		for(int i=1;i<=10;i++){
			//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
			threadPool.execute(()->{
				System.out.println(Thread.currentThread().getName()+"\t 办理业务");
			});
		}
	} catch (Exception e) {
		// TODO: handle exception
	}finally{
		threadPool.shutdown();
	}	}

在这里插入图片描述

2.2.3.newCachedThreadPool

Exectors.newCachedThreadPool()–>执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重用他们。可扩容,遇强则强。一池n线程,可扩容,可伸缩,cache缓存的意思
那么池的数量应该设置多少呢,如果银行只有一个窗口,那么当人来得太多了,就忙不过来。如果银行有很多个窗口,但是人来的少,此时又显得浪费资源。那么如何该合理安排呢?这就需要用到newCachedThreadPool()方法,可扩容,可伸缩

public static void main(String[] args) {
		//一池一个工作线程,类似一个银行有n个受理窗口
		ExecutorService threadPool=Executors.newCachedThreadPool(); 	
		try {
			//模拟有10个顾客过来银行办理业务
			for(int i=1;i<=10;i++){
				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
				threadPool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}

在这里插入图片描述

public static void main(String[] args) {
		//一池一个工作线程,类似一个银行有n个受理窗口
		ExecutorService threadPool=Executors.newCachedThreadPool(); 	
		try {
			//模拟有10个顾客过来银行办理业务
			for(int i=1;i<=10;i++){
				try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
				threadPool.execute(()->{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}

在这里插入图片描述

3.ThreadPoolExecutor底层原理

newFixedThreadPool底层源代码

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

可以看到,底层的参数包含LinkedBlockingQueue阻塞队列。

newSingleThreadExecutor底层源代码

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

newCachedThreadPool底层源代码

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

SynchronousQueue这个阻塞队列是单一版阻塞队列,阻塞队列的容量为1.

这3个方法其实都共同返回了一个对象,即ThreadPoolExecutor的对象

4.线程池7大重要参数

ThreadPoolExecutor的构造函数

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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

上面的int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler即我们的七大线程参数
上面是ThreadPoolExecutor类的构造方法,有7大参数:

1)corePoolSize:线程池中的常驻核心线程数,简称核心数。
比如说,一个线程池我们可以把它当作银行的网点,银行只要开门,就必须至少有一个人在值班,这个就叫常驻核心线程数。比如如果某个银行周一到周五五个网点全开,那么周一到周五的常驻核心线程数为5.如果今天业务没有那么频繁,窗口为1,那么今天的常驻核心线程数就是1

2)maxImumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1

3)keepAliveTime:多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到剩下corePoolSize为止
如果线程池中有常驻线程数,又有最大线程数,说明平时是用常驻的,工作紧张了,它会扩容到最大线程数,如果业务降下来了,我们设置了多余的空闲线程的存活时间,比如设置30s,如果30s都没有多余的请求过来,有些银行就会关闭窗口,所以它不仅会扩大还会缩小。

4)unit:keepAliveTime的单位
单位:是秒,毫秒,微秒。

5)workQueue:任务队列,被提交但尚未被执行的任务
这是一个阻塞队列,比如说银行,只有3个受理窗口,而来了4个客户。这个阻塞队列就是银行的候客区,来了客户不能让他走了。窗口数控制了线程的并发数。

6)threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认即可
线程都是统一的创建。线程池里面有已经new好的线程,这些由线程池工厂生产。

7)handler:拒绝策略,表示当前队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略

比如说今天银行客流高峰,三个窗口都满了,候客区也满了。我们没有选择继续拉人,因为不安全我们选择委婉的拒绝。

在下一节我们将介绍线程池底层工作原理

相关学习推荐:java基础

以上是Java讲解ThreadPool线程池的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Laravel 教程
1604
29
PHP教程
1510
276
如何在Java中实现简单的TCP客户端? 如何在Java中实现简单的TCP客户端? Aug 08, 2025 pm 03:56 PM

Importjava.ioandjava.net.SocketforI/Oandsocketcommunication.2.CreateaSocketobjecttoconnecttotheserverusinghostnameandport.3.UsePrintWritertosenddataviaoutputstreamandBufferedReadertoreadserverresponsesfrominputstream.4.Usetry-with-resourcestoautomati

用Docker将Java应用程序部署到Kubernetes 用Docker将Java应用程序部署到Kubernetes Aug 08, 2025 pm 02:45 PM

容器化Java应用:创建Dockerfile,使用基础镜像如eclipse-temurin:17-jre-alpine,复制JAR文件并定义启动命令,通过dockerbuild构建镜像并用dockerrun测试本地运行。2.推送镜像到容器注册表:使用dockertag标记镜像并推送到DockerHub等注册表,需先登录dockerlogin。3.部署到Kubernetes:编写deployment.yaml定义Deployment,设置副本数、容器镜像和资源限制,编写service.yaml创建

VS代码快捷方式专注于Explorer面板 VS代码快捷方式专注于Explorer面板 Aug 08, 2025 am 04:00 AM

VSCode中可通过快捷键快速切换面板与编辑区。要跳转至左侧资源管理器面板,使用Ctrl Shift E(Windows/Linux)或Cmd Shift E(Mac);返回编辑区可用Ctrl `或Esc或Ctrl 1~9。相比鼠标操作,键盘快捷键更高效且不打断编码节奏。其他技巧包括:Ctrl KCtrl E聚焦搜索框,F2重命名文件,Delete删除文件,Enter打开文件,方向键展开/收起文件夹。

修复:Windows Update无法安装 修复:Windows Update无法安装 Aug 08, 2025 pm 04:16 PM

runthewindowsupdatetrubloubleshooterviaSettings>更新&安全> is esseShootsoAtomationfixCommonissues.2.ResetWindowSupDateComponentsByStoppingRealatedServices,RenamingTheSoftWaredWaredWaredSoftwaredSistribution andCatroot2Folders,intrestrestartingthertingthertingtherserviceSteStoceTocle

如何在Java中使用一个时循环 如何在Java中使用一个时循环 Aug 08, 2025 pm 04:04 PM

AwhileloopinJavarepeatedlyexecutescodeaslongastheconditionistrue;2.Initializeacontrolvariablebeforetheloop;3.Definetheloopconditionusingabooleanexpression;4.Updatethecontrolvariableinsidethelooptopreventinfinitelooping;5.Useexampleslikeprintingnumber

Java对象的序列化过程是什么? Java对象的序列化过程是什么? Aug 08, 2025 pm 04:03 PM

JavaserializationConvertSanObject'SstateIntoAbyTeSteAmForStorageorTransermission,andDeserializationReconstructstheObjectStheObjectFromThstream.1.toenableserialization,aclassMustimustimplementTheSerializableizableface.2.UseObjectObjectObjectObjectOutputputputputputtreamToserialializeanobectizeanobectementeabectenobexpent,savin

什么是Java的哈希图? 什么是Java的哈希图? Aug 11, 2025 pm 07:24 PM

ahashmapinjavaiSadattrastureturethatStoreskey-valuepairsforefficeFitedReval,插入和deletion.itusesthekey’shashcode()methodtodeTermInestorageLageLageAgeLageAgeAgeAgeAgeAneStorageAgeAndAllowSavereo(1)timecomplexityforget()

python numpy阵列示例 python numpy阵列示例 Aug 08, 2025 am 06:13 AM

NumPy数组的使用包括:1.创建数组(如从列表、全零、全一、范围创建);2.形状操作(reshape、转置);3.向量化运算(加减乘除、广播、数学函数);4.索引与切片(一维和二维操作);5.统计计算(最大值、最小值、均值、标准差、求和及轴向操作);这些操作高效且无需循环,适合大规模数值计算,最终掌握需多加练习。

See all articles