线程池的基本概念

Posted 杀手不太冷!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程池的基本概念相关的知识,希望对你有一定的参考价值。

文章目录

线程池

前言

平时讨论多线程处理,一定会说使用线程池,那为什么要使用线程池呢?其实,这个问题也可以反过来思考一下,如果不使用线程池会怎么样?

当需要多线程并发执行任务时,只能不断的通过new Thread创建线程,每创建一个线程都需要在堆上分配内存空间,同时需要分配虚拟机栈,本地方法栈,程序计数器等线程私有的内存空间,当这个线程对象被可达性分析算法标记为不可用时,被GC回收,这样频繁的创建和回收需要大量的额外开销。再者说,JVM的内存资源是有限的,如果系统中大量的创建线程对象,JVM很可能直接抛出OutOfMemoryError异常,还有大量的线程去竞争CPU会产生其他的性能开销,更多的线程反而会降低性能,所以必须要限制线程数。

既然不使用线程池会有那么多问题,我们来看一下使用线程池有哪些好处:

1.使用线程池可以复用池中的线索,不需要每次都创建新线程,减少创建和销毁线程的开销。

2.同时,线程池具有队列缓冲策略,拒绝机制和动态管理线程个数,特定的线程池还具有定时执行,周期执行功能,比较重要的一点是线程池可以实现线程环境的隔离,例如分别定义支付功能相关线程池和优惠券功能相关线程池,当其中一个运行有问题时不会影响另一个。

一个线程池里面有多个线程,因此我们后续都是用线程池对象的方法进行多线程操作的,这样我们只针对线程池对象就行了,不必再针对线程对象了,因此我们就不用频繁的创建销毁线程了。

线程池的类结构图

如下图:

线程池状态

Running:线程池运行状态

Shutdown状态:线程池不会再接收新的任务,但会处理阻塞队列剩余任务

Stop状态:会中断正在执行的任务,并抛弃阻塞队列任务

Tidying状态:任务全部执行完毕,活动线程为0即将进入终结

Terminated状态:终结状态。

ThreadPoolExecutor构造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize 核心线程数目,最多保留的线程数

maximumPoolSize 最大线程数目

keepAliveTime 生存时间-针对救急线程

unit 时间单位-针对救急线程

workQueue 阻塞队列

threadFactory 线程工厂-可以为线程创建时起个好名字

handler 拒绝策略

最大线程数=核心线程数+救急线程数,其中核心线程不会消失永远存在,而救急线程是会消失的。

使用newFixedThreadPool方法给线程池创建固定线程数

newFixedThreadPool方法可以在线程池里面创建固定的线程个数,如下图:

当我们调用我们的线程池执行一个任务的时候,线程池会调用它里面的线程此任务,如下图:

提交任务方法submit

//执行任务
void execute(Runnable command);

//提交任务task,用返回值Future获得任务执行结果
<T> Future<T> submit(Callable<T> task);

//提交tasks中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
    throws InterruptedException;

//提交tasks中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extend Callable<T>> tasks,
                             long timeout,TimeUnit unit)
    throws InterruptedException;

//提交tasks中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其他任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
    throws InterruptedException;

//提交tasks中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其他任务取消,带超时时间
<T> T invokeAny(Collection<>? extends Callable<T>> tasks,
               long timeout,TimeUnit unit)
    throws InterruptedException;

当我们使用submit给线程池提交任务的时候,线程池会用它里面的不同的线程去并发执行这些任务,如下图:

submit方法提交任务的时候,有两种类型的参数,一种是Callable线程任务,这种任务有返回值并且可以内部抛异常,另外一种是Runnable线程任务,这种任务没有返回值并且不可以在内部抛异常。但是Callable和Runnable对象都属于是多线程任务对象。Callable线程对象对应的线程方法是call方法,而Runnable线程对象对应的方法是run方法。

对于任务来讲,只要它一提交到线程池里面,它就会被自动的用线程池里面的线程执行了。

使用submit提交的线程任务,一般会在下面的普通代码执行之后执行,如下图:

关闭线程池方法shutdown

/**
	线程池状态变为 shutdown
	不会接收新任务
	但已提交的任务会执行完
	此方法不会阻塞调用线程的执行
*/
void shutdown();

/**
	线程池状态变为stop
	不会接收新任务
	会将队列中的任务返回
	并用interrupt的方式中断正在执行的任务
*/
List<Runnable> shutdownNow();


创建多线程的几个方法

直接继承Thread

实现Runnable接口

实现Callable接口

jdk1.8新增线程池

使用偷窃线程池的时候,线程池中的每个线程都有一个任务队列,线程之前可以互相帮助,如果一个线程的工作已经干完了,发现另外一个线程的工作还没有干完,那么这个线程会去帮另外一个线程工作。这个线程池非常适合于耗时较长的任务。

以上是关于线程池的基本概念的主要内容,如果未能解决你的问题,请参考以下文章

线程池的基本概念

006 线程池

c# 并发编程系列之一:线程进程线程池的基本概念

自己实现一个简单的线程池

线程池的基础与操作

线程池Executors详解