java 线程池

sancaiodm Java 2021-09-19 1274 0

线程池的定义?

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,就把任务提交到线程池中的任务队列,在处理完任务之后线程并不会被销毁,需是仍然在线程池中等待提交的任务,线程池的优点就是很好的避免频繁创建一销毁线程。


什么时候使用线程池?

1  单个任务处理时间比较短

2  需要处理的任务数量很大

  

下图是java 线程池的继承树:

image.png

   

Executor:一个运行新任务的简单接口;其内容只有一个抽象方法: execute();

ExecutorService:扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法;

ScheduledExecutorService:扩展了ExecutorService。支持Future和定期或循环执行任务。

Executor接口

public interface Executor {

    void execute(Runnable command);

}

Executor接口只有一个execute方法,用来替代通常创建或启动线程的方法。例如,使用Thread来创建并启动线程的代码如下:

Thread t = new Thread();

t.start();

使用Executor来启动线程执行任务的代码如下:

Thread t = new Thread();

executor.execute(t);

对于不同的Executor实现,execute()方法可能是创建一个新线程并立即启动,也有可能是使用已有的工作线程来运行传入的任务,也可能是根据设置线程池的容量或者阻塞队列的容量来决定是否要将传入的线程放入阻塞队列中或者拒绝接收传入的线程。

ExecutorService接口

ExecutorService接口继承自Executor接口,提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。增加了shutDown(),shutDownNow(),invokeAll(),invokeAny()和submit()等方法。如果需要支持即时关闭,也就是shutDownNow()方法,则任务需要正确处理中断。

ScheduledExecutorService接口

ScheduledExecutorService扩展ExecutorService接口并增加了schedule方法。调用schedule方法可以在指定的延时后执行一个Runnable或者Callable任务。ScheduledExecutorService接口还定义了按照指定时间间隔定期执行任务的scheduleAtFixedRate()方法和scheduleWithFixedDelay()方法。

ThreadPoolExecutor类

ThreadPoolExecutor继承自AbstractExecutorService,也是实现了ExecutorService接口。

ScheduledThreadPoolExecutor类

ScheduledThreadPoolExecutor类继承自ThreadPoolExecutor同时实现了ScheduledExecutorService接口,在ThreadPoolExecutor的基础上支持执行延迟任务或者周期性的任务。

Executors类

Executors类是java.util.concurrent包下的一个类,提供了一系列静态方法,用于生成不同类型的线程池。Executors一共可以创建下面这四类线程池:

1 而使用Executors方式是指通过Executors工具类不同静态方法来创建线程池

image.png

     而在《阿里巴巴java开发手册》中指出线程池是不允许使用Executors去创建,要求通过ThreadPoolExecutor方式,因为jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外原因是前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

2  使用  ThreadPoolExecutor方式是指通过ThreadPoolExecutor类的构造方法来创建线程池。

image.png


ExecutorService executorService = Executors.newSingleThreadExecutor(); //创建一个单线程

private static ExecutorService pool;   pool = new ThreadPoolExecutor(1, 2,...);

----------------------------------华丽分割线---------------------------------------------   

ScheduledThreadPoolExecutor scheduledThreadPool = new ScheduledThreadPoolExecutor(3);

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

scheduledThreadPool.schedule(runnableTask, 10, TimeUnit.SECONDS);

线程池的创建部分需结合接口多态来了解,要不然很绕。


   启动所有的线程后,执行线程池的 shutdown()方法并不是关闭任何线程,此方法只是对线程池的操作,其代表所有的线程执行完成后关闭此线程池,可以通过isTerminated()方法或是isShutdown()方法来判断,线程池是否已关闭,线程池的关闭则意味中线程池中的所有线程都已运动完成。


如何为线程池选择合适线程数?


   线程任务一般可以分为计算密集型IO密集型
  计算密集型CPU处于忙碌中,此时需要做内存数据读写计算,没有任务的阻塞状态。而IO密集型任务,在执行IO时堵塞,CPU处于等待状态,等待过程中系统会将时间片分给其他线程处理。
  计算密集型任务线程数最好与系统核心数挂钩,毕竟4核单线程主机在某一时刻只能同时跑4个线程,如果过多的线程数反而会因为切换上下文而耗费更多的任务时间,可以通过调用JDK自带的方法Runtime.availableProcessors获得系统支持的可以核心数。N+1。
  对于IO密集型,合适的线程数可以获得良好的性能支持,系统通过将IO阻塞的任务线程的时间片交给未被阻塞的任务线程,通过合适的调度发挥出比单线程更好的性能支持。在选择线程数应该考虑内存支持程度,避免过多的线程数导致内存激增产生OOM。2N+1。
  线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。



什么是线程池?

Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor,ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

线程池的优点:

线程池的出现,恰恰就是解决上面类似问题的痛点,而线程池的优点有:

(1)复用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。

(2)能够有效的控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。

(3)能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。


线程池

主要分为四类

FixedThreadPool:只有核心线程数,反应快

CacheThreadPool:只有非核心线程数,数量不定,超时限制60秒,适合只想大量耗时较小的任务

ScheduledThreadPool:核心线程固定,非核心线程不定,主要用于执行定时任务和具有周期的重复任务

SingleThreadExecutor:只有一个核心线程,不需要处理同步问题

但本质都是间接或直接通过配置ThreadPoolExecutor来实现的


ThreadPoolExecutor

主要参数

corePoolSize:核心线程,默认情况下一直存活

maximumPoolSize:最大线程数,超过的话,后续新任务被阻塞

keepAliveTime:非核心线程的超时时长,超过会被回收

workQueue:任务队列,通过线程池中的execute()提交的Runnable对象会存储在这里

在AsyncTask中有明显的显示:

核心线程数等于CPU核心数+1

最大线程数等于CPU核心数*2+1

非核心线程超时设置为1秒

任务队列容量128

原文链接:https://blog.csdn.net/l540675759/article/details/62230562



好文推荐

深入理解Java线程池:ThreadPoolExecutor

评论