美文网首页
java多线程学习5

java多线程学习5

作者: fdsun | 来源:发表于2020-09-10 22:23 被阅读0次

线程池 ThreadPoolExecutor

官方API解释线程池的好处:

(1)通过重用线程池中的线程,来减少每个线程创建和销毁的性能开销。

(2)对线程进行一些维护和管理,比如定时开始,周期执行,并发数控制等等。

一、Executor

Executor是一个接口,跟线程池有关的基本都要跟他打交道。下面是常用的ThreadPoolExecutor的关系。

Executor <- ExecutorService <- AbstractExecutorService <- ThreadPoolExecutor

Executor接口很简单,只有一个execute方法。

ExecutorService是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。

AbstractExecutorService是一个抽象类。ThreadPoolExecutor就是实现了这个类。

二、ThreadPoolExecutor

ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

构造方法参数讲解

参数名 作用
corePoolSize 核心线程池大小
maximumPoolSize 最大线程池大小
keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
TimeUnit keepAliveTime时间单位
workQueue 阻塞任务队列
threadFactory 新建线程工厂
RejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

2、ThreadPoolExecutor类中有几个非常重要的方法

//主要是这四个方法
execute()
submit()
shutdown()
shutdownNow()

(1)execute()

execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。

它的主要意思就是:

    任务提交给线程池之后的处理策略,这里总结一下主要有4点
当线程池中的线程数小于corePoolSize 时,新提交的任务直接新建一个线程执行任务(不管是否有空闲线程) 
当线程池中的线程数等于corePoolSize 时,新提交的任务将会进入阻塞队列(workQueue)中,等待线程的调度 
当阻塞队列满了以后,如果corePoolSize < maximumPoolSize ,则新提交的任务会新建线程执行任务,直至线程数达到maximumPoolSize 
当线程数达到maximumPoolSize 时,新提交的任务会由(饱和策略)管理

(2)submit()

​ submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。

(3)shutdown()和shutdownNow()

​ 如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;

还有很多其他的方法:

比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等获取与线程池相关属性的方法,有兴趣的朋友可以自行查阅API。

三.使用示例

(略)

通过案例总结:

当线程数小于核心线程数(5)时会创建新线程,如果要执行的线程大于5,就先把任务放入队列中,如果队列最大容量5已经满了,那会在创建线程,直到最大达到最大线程数10。

注意

这里如果创建超过15个,比如将for循环中改成执行20个任务,就会抛出任务拒绝异常了。因为你的队列和最大线程数才15,如果有20个任务就会抛异常。

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池

Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor();   //创建容量为1的缓冲池
Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池

下面是这三个静态方法的具体实现;

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

四、用线程池和不用线程池的区别是什么?

(略)

小结:相比于独立创建线程,线程池创建线程需要的内存空间更小,需要的时间更少

相关文章

  • Java多线程学习(三)——synchronized(上)

    在前两节的《Java多线程学习(一)——多线程基础》和《Java多线程学习(二)——Thread类的方法介绍》中我...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • java多线程学习5

    线程池 ThreadPoolExecutor 官方API解释线程池的好处: (1)通过重用线程池中的线程,来减少每...

  • JAVA

    学习了JAVA中的多线程

  • (1)如何开启多线程

    前言 学习java多线程首先就是要知道如何开启多线程.在java中开启多线程主要有两种方式: 继承Thread类,...

  • java高效并发学习笔记(一)java内存模型

    java高效并发学习笔记(一)java内存模型 学习JVM+JAVA多线程中,学习的书籍是《深入理解java虚拟机...

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java 多线程 - Java 内存模型

    前言 学习Java多线程,要了解多线程可能出现的并发现象,了解Java内存模型的知识是必不可少的。 对学习到的重要...

  • java内存模型

    前言 在学习java多线程并发编程前,必须要了解java内存模型,只有了解java内存模型,才能知道为什么多线程并...

网友评论

      本文标题:java多线程学习5

      本文链接:https://www.haomeiwen.com/subject/hdcjektx.html