Java 并发编程(线程池)(5)

概述:

数据库链接池: 每次与数据链接, 创建连接对象Connection 操作完之后,进行销毁 频繁创建销毁比较耗时.创建一个池子,预先在池子中初始化好一部分连接(Connection )对象, 使用时直接获取即可,用完还回,不需要频繁创建销毁.

线程池和数据库连接池的思想是一样的:

以前我们需要使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁, 而是可以继续执行其他的任务?

Java 中可以通过线程池来达到这样的效果。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

为什么使用线程池?

并发量大的情况下,频发创建销毁线程开销较大.创建线程池缓解压力.

jdk5之后,提供ThreadPoolExecutor类来实现线程池创建, 是建议被使用的(见《阿里巴巴 java 开发规范》), 里面有7个参数来设置对线程池的特征的定义.

在这里插入图片描述

ThreadPoolExecutor 类:

java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类.

ThreadPoolExecutor 继承了 AbstractExecutorService 类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。

public class ThreadPoolExecutor extends AbstractExecutorService {
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
     public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
     public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
     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;
    }
}

构造器中各个参数的含义:

1.corePoolSize:

核心线程池数量, 在创建后核心线程池数量默认为0, 有任务来了后,才会去创建线程去执行, 或者调用prestartAllCoreThreads()或者 prestartCoreThread()方法,进行预创建.

2.maximumPoolSize:

线程池总数量,表示线程池最大能装多少个线程.

3.keepAliveTime:

指的是非核心线程池中的线程,在没有任务执行时空闲多长时间后销毁。

4.unit:

参数 keepAliveTime 的时间单位,有 7 种取值,在 TimeUnit 类中有 7 种静态属性:
在这里插入图片描述

5.workQueue:

一个阻塞队列,用来存储等待执行的任务,可以自己来指定等待队列的实现类。这个参数的选择也很重要,会对线程池的运行过程产生重大影响。

6.threadFactory:

线程工厂,主要用来创建线程;

7.handler:

表示当拒绝处理任务时的策略.

线程池的执行:

创建完成 ThreadPoolExecutor 之后,当向线程池提交任务时,通常使用 execute 方法。 execute 方法的执行流程图如下:

在这里插入图片描述

1.如果线程池中存活的核心线程数小于线程数 corePoolSize 时,线程池会创建一个核心线程去处理提交的任务。

2.如果线程池核心线程数已满,即线程数已经等于 corePoolSize,一个新提交的任务,会被放进任务队列 workQueue 排队等待执行。

3.当线程池里面存活的线程数已经等于 corePoolSize 了,并且任务队列 workQueue 也满,判断线程数是否达到 maximumPoolSize,即最大线程 数是否已满,如果没到达,创建一个非核心线程执行提交的任务。

4.如果当前的线程数达到了 maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

线程池中的队列:

线程池有以下工作队列:

ArrayBlockingQueue:有界队列,是一个用数组实现的有界阻塞队列,按 FIFO 排序量。

LinkedBlockingQueue:可设置容量队列,基于链表结构的阻塞队列,按 FIFO 排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列, 最大长度为 Integer.MAX_VALUE,吞吐量通常要高于 ArrayBlockingQuene;

线程池的拒绝策略:

构造方法的中最后的参数 RejectedExecutionHandler 用于指定线程池的拒绝策略。当请求任务不断的过来,而系统此时又处理不过来的时候,我们就需要采取对应的策略是拒绝服务。

默认有四种类型:

AbortPolicy 策略:该策略会直接抛出异常,阻止系统正常工作。

CallerRunsPolicy 策略:只要线程池未关闭,该策略在调用者线程中运行当前的任务(如果任务被拒绝了,则由提交任务的线程(例如:main)直接执行此任务)。

DiscardOleddestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。

DiscardPolicy 策略:该策略丢弃无法处理的任务,不予任何处理。

execute 与 submit 的区别:

执行任务除了可以使用 execute 方法还可以使用 submit 方法。

它们的主要区别是:

execute 适用于不需要关注返回值的场景,submit 方法适用于需要关注返回值的场景。

线程池的关闭 :

关闭线程池可以调用 shutdownNow 和 shutdown 两个方法来实现。

shutdownNow:对正在执行的任务全部发出 interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表。

shutdown:当我们调用 shutdown 后,线程池将不再接受新的任务,但也不会 去强制终止已经提交或者正在执行中的任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值