• 周五. 12月 2nd, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

线程池

admin

11月 28, 2021

(一)概述

1、没有线程池的状态:

当我们使用一条线程的时候,先将线程对象创建出来,启动线程,在运行过程中,可能

能完成任务,也可能会在中途被任务内容中断掉,任务还没有完成。

即使是能够正常完成,线程对象就结束了,就变成了垃圾对象,需要被垃圾回收器回收

如果在系统中,大量的任务都是小任务,任务消耗时间较短、线程对象的创建和消亡耗

费的时间比较多,结果:大部分的时间都浪费在了线程对象的创建和死亡上。

如果任务本身破坏力比较大,可能会把线程对象结束掉,就无法继续完成任务。

2、有线程池的状态

在没有任务的时候,先把线程对象准备好,存储到一个容器中,一旦有任务来的时候,

就不需要创建对象,而是直接将对象获取出来执行任务

如果任务破坏力较小,任务可以直接完成,这个线程对象不会进入死亡状态,而是被容

器回收,继续活跃。

如果任务破坏力较大,任务会把线程搞死,线程池会继续提供下一个线程,继续完成这

个任务。

(二)使用

1、步骤:获取线程池对象;创建任务类对象;将任务类对象提交到线程池中

2、获取线程池对象:

工具类:Executors:生成线程池的工具类,根据需求生成指定大小的线程池

ExecutorService Executors.newSingleThreadPool():创建一个有单个线程的线程池

ExecutorService Executors.newFixedThreadPool(int nThreads):创建一个指定线

程数量的线程池

3、创建任务类对象:Runnable的实现类对象,用于定义任务内容

4、将任务类对象提交到线程池中ExecutorService:是一个接口,不需要手动创建这个接口的实现类对象,使用方法获取

到的就是这个接口的实现类对象,一定可以调用这个接口中的方法

submit(Runnable r):可以将一个任务类对象,提交到线程池中,如果有空闲的线程,

就可以马上运行这个任务,如果没有空闲线程,那么这个任务就需要等待。

shutDown():结束线程池,已经提交的全部保证完成,不准继续提交了

shutDownNow():结束线程池,已经开始运行的,保证完成;但是还没有运行的,已经提

交的,不给运行了,作为返回值范围;对于没有提交的,不准提交。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo15_UseThreadPool {

    public static void main(String[] args) {
        //线程池中线程的默认名称:pool-1-thread-1

        /*
        * 当提交的任务数量多于池中线程数,则先给所有线程安排任务去执行,哪一个线程先执行完了自己的任务,就立马执行排队的任务
        *
        * */
        //1.获取线程池:ThreadPoolExecutor
        ExecutorService es = Executors.newFixedThreadPool(2);

        //2.将任务提交到线程池
        //submit(Runnable task)
        Runnable task1 = new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("AAAAAAAAAAAAAAAAA" + Thread.currentThread().getName());

                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Runnable task2 = new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("@@@@@@@@@@@@@@@@@" + Thread.currentThread().getName());

                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Runnable task3 = new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("5555555555555555" + Thread.currentThread().getName());

                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        es.submit(task1);
        es.submit(task2);
        es.submit(task3);

        //关闭线程池,执行已经提交的任务和正在排队的任务,不接受新任务
        es.shutdown();

        //关闭线程池,尝试结束正在执行的任务,不执行正在排队的任务,不接受新任务
        //es.shutdownNow();

    }
}

线程池原理

(一)线程池部分体系

1、Executor:接口,定义了一个接收 Runnable 对象的方法executor(Runnable

command)

2、ExecutorService:比 Executor 使用更广泛的子接口

3、AbstractExecutorService:ExecutorService 抽象方法的实现类

4、ThreadPoolExecutor:线程池,可以通过调用 Executors 工具类的静态工厂

方法来创建线程池并返回一个 ExecutorService 的实现类对象

(二)ThreadPoolExecutor类概述

1、Executors工具类,是创建线程池的工具类

2、ExecutorService接口,是工具类返回线程池对象所实现的接口

3、ThreadPoolExecutor是ExecutorService接口的实现类,就是返回的线程池对

象所属类

4、ThreadPoolExecutor的构造方法:

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

          用给定的初始参数创建新的 ThreadPoolExecutor

ThreadPoolExecutor(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

corePoolSize:

线程池的核心线程数。在创建了线程池后,默认情况下,

线程池中并没有任何线程,当有任务来之后,就会创建一个线程去执行任务,

当线程池中的线程数目达到 corePoolSize 后,

就会把到多出的任务放到队列当中

maximumPoolSize:

线程池允许创建的最大线程数。

如果队列满了,并且已创建的线程数小于最大线程数,

则线程池会再创建新的线程执行任务。

注意:如果使用了无界的任务队列这个参数就没什么效果。

keepAliveTime:

线程活动保持时间。线程池的工作线程空闲后,保持存活的时间。

如果任务很多,并且每个任务执行的时间比较短,

可以调大这个时间,提高线程的利用率。

unit:

参数 keepAliveTime 的时间单位,在TimeUnit枚举类中,有 7 种取值。

可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),

毫秒(MILLISECONDS),微秒(MICROSECONDS),纳秒(NANOSECONDS)

用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS

workQueue:

任务队列。用于保存等待执行的任务的阻塞队列。

可以选择有界队列和无界队列

threadFactory:

线程工厂,用来创建线程。主要是为了给线程起名字,

默认工厂的线程名字:pool‐1‐thread‐1

Executors工具类中的defaultThreadFactory()方法可以获取到

用于创建新线程的默认线程工厂

handler:

拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用

(三)阻塞队列和有界、无界队列

1、阻塞队列:是一个支持阻塞的插入和移除方法的队列

(1)支持阻塞的插入方法:当队列满时,队列会阻塞插入元素的线程,直

到队列不满

(2)支持阻塞的移除方法:当队列为空时,获取元素的线程会等待队列变

为非空

2、有界队列:具有固定大小的队列,可以进队的元素个数是有限的。

3、无界队列:是没有设置固定大小的队列。这些队列的特点是可以直接入列,

直到内存资源耗尽

4、相关类型即构造方法:

(1)ArrayBlockingQueue 基于数组实现的有界阻塞队列

(2)LinkedBlockingQueue 基于链表实现的队列,可以设定为有界,不设

定则无界

(3)SynchronousQueue 无界无缓冲的队列,内部容量为零,适用于元素数

量少的场景

(四)拒绝策略

1、ThreadPoolExecutor.AbortPolicy

(此项是默认策略)直接抛出异常RejectedExecutionException

2、ThreadPoolExecutor.CallerRunsPolicy

该任务被线程池拒绝,由调用 execute方法的线程执行该任务

3、ThreadPoolExecutor.DiscardOldestPolicy

抛弃队列最前面的任务,然后重新尝试执行任务

4、ThreadPoolExecutor.DiscardPolicy

丢弃任务,也不会抛出异常

(五)线程池执行流程

(六)有界队列和无界队列对线程池的影响

1、使用有界队列:如果有新的任务需要执行,如果线程池时机线程数量小于核

心线程数corePoolSize,则优先创建线程。如果大于核心线程数corePoolSize,

则会将任务加入队列。如果队列已满,则在总线程数量不大于最大线程数

maximumPoolSize的前提下,创建新的线程。如果线程数大于最大线程数

maximumPoolSize,则执行拒绝策略

2、使用无界队列:除了系统资源耗尽,否则无界队列不存在任务入队失败的情

况。当有任务到来,系统的线程数小于核心线程数corePoolSize时,新建线程执

行任务。如果后续仍然有新的任务加入,而没有空闲的线程,则任务直接入队等

待。如果任务处理慢,而任务添加快,则队列会快速增长,直到系统资源耗尽。

(七)ThreadPoolExecutor源码分析

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注