并发编程(三)线程池原理和应用

线程池原理和应用

-- 1、线程池的创建和常用参数分析

1、创建线程池的参数释义

2、线程池状态分析

创建线程池参数

                                               利用Executors创建一个固定数量的线程池

                                                                            线程池构造函数

线程池参数

corePoolSize

核心线程数,保持在线程池中线程的数量

maximumPoolSize

线程池允许的最大线程数

keepAliveTime/timeUnit

线程池中线程空闲不被释放的最大时间,配合timeUnit使用,为0表示永远不被释放。

注:

当多余核心线程数的线程空闲时间超过keepAliveTime(0除外),线程仍没有任务去执行,会将这部分线程释放掉。

如果设置了allowCoreThreadTimeOut,当核心线程空闲超时后,也会被释放

workQueue

BlockingQueue<Runnable>,工作线程任务的阻塞队列,用来存放等待执行的任务, 默认实现:LinkedBlockingQueue

threadFactory

线程池创建工厂,子类通过自定义实现接口“Thread newThread(Runnable r)”通过工厂创建线程池具体的Thread线程。

默认实现:DefaultThreadFactory

线程池参数—默认线程池工厂

线程池参数—拒绝策略

handler(RejectedExecutionHandler)

当workQueue无法存放新加任务,或添加新任务后线程池停止工作,使用设置的拒绝策略拒绝新加任务的执行,可以用rejectedExecution来实现自己的拒绝策略。

默认拒绝策略:AbortPolicy,直接抛出异常

常见拒绝策略

CallerRunsPolicy:调用方执行策略,当前调用线程或添加任务的线程执行,这种方式当线程池无法执行时,使用调用方资源来执行任务

AbortPolicy:异常策略,直接抛出RejectedExecutionException异常

DiscardPolicy:直接抛弃策略,对任务不做任何事情,忽略该任务,不执行不报错

DiscardOldestPolicy:抛弃最早任务策略,将workQueue的一个任务取出抛弃,将当前任务放入workQueue中执行

线程池状态

RUNNING:初始状态,运行中

SHUTDOWN:关闭状态, shutdown()方法后变为该状态,不再接受新任务,仍处理已添加任务

STOP:停止状态,调用shutdownNow()方法后会从RUNNING状态进入到这个状态,此时不接受新任务,并且会将执行中的线程中断

TIDYING:整理状态,此时队列中任务数量已经是0

TERMINATED:终结状态,由TIDYING状态后调用terminated()后进入该状态

线程池状态流转

-- 2、常用线程池及线程池执行过程分析

1、了解Java SDK的常用线程池

2、线程池执行过程分析

Java常见线程池

  1. 固定线程数量的线程池
  2. 单线程的线程池
  3. 可缓存的线程池
  4. 定时执行的线程池

固定线程数量的线程池

通过Executors.newFixedThreadPool来创建

核心线程数和最大线程数一样

达到核心线程数后,空闲线程不会超时被终止或释放

每添加一个任务后,会将任务添加到工作任务队列,线程池创建一个线程,线程数等于核心线程数时,就不会再创建线程

单线程的线程池

通过Executors.newSingleThreadExecutor来创建

核心线程数和最大线程数一样,且均为1,也就是只有一个线程在执行队列的工作任务

使用了代理模式来创建线程池

可缓存的线程池

通过Executors.newCachedThreadPool来创建

核心线程数为0,最大线程数为无限大(实际是int最大值),空闲线程可以缓存60s,空闲超过60s的线程会被回收

使用了SynchronousQueue同步队列,添加任务的同时,需有工作线程来取任务才可完成任务的添加和执行

定时执行的线程池

通过Executors.newScheduledThreadPool来创建

可指定核心线程数,最大线程数为无限大(实际是int最大值),核心线程数空闲不会超过被回收

使用了DelayedWorkQueue延时队列,通过延时队列来控制时间来执行

线程池执行过程

                                                                                      运行线程池案例

                                                ThreadPoolExecutor.execute

execute底层执行步骤

1、首先来检查线程池的运行状态和工作线程数量,如果工作线程的线程数少于核心线程数,则会创建一个新的线程来执行给定的任务,通过调用addWorker来执行任务。

        int c = ctl.get();

        if (workerCountOf(c) < corePoolSize) {

            if (addWorker(command, true))

                return;

            c = ctl.get();

        }

2.如果超过了核心线程数量,把任务放到工作任务队列中,若工作队列没有满,则添加任务后,仍需要重新检查一下线程池的状态,如果没有继续运行(不是RUNNING状态)就把任务移除,使用拒绝策略来处理当前任务;否则将创建或唤醒工作线程来执行任务

        if (isRunning(c) && workQueue.offer(command)) {

            int recheck = ctl.get();

            if (! isRunning(recheck) && remove(command))

                reject(command);

            else if (workerCountOf(recheck) == 0)

                addWorker(null, false);

        }

3.线程池非RUNNING状态或添加任务失败后,使用拒绝策略来处理当前任务

        else if (!addWorker(command, false))

            reject(command);

线程池addWorker执行过程

boolean addWorker(Runnable firstTask, boolean core) 方法

返回true表示接收了任务,否则表示拒绝任务

注:

(1)如果不是RUNNING状态,(2)如果是SHUTDOWN状态且接收了新任务,满足这些条件则返回false

判断工作线程的数量,如果是添加core线程任务,则判断工作线程数量与核心线程数量大小,否则判断最大线程数量大小,如果超过了的话,返回false

线程池状态发生改变之后,重新执行这个for循环的代码块

注:

传入任务创建Worker对象,使用线程池的锁来加锁,来保证添加工作线程的过程是线程安全的

将创建的工作线程加入到HashSet<Worker> workers中,用于保存已经创建的工作线程

解锁,调用worker.thread的start()方法执行任务

1、实际执实际执行方法ThreadPoolExecutor#runWorker,最终执行时是执行我们最开始创建的Runnable对象的run方法

2、执行任务,Java充分对各种阶段留好钩子,方便业务自己实现钩子定制,如beforeExecute()、afterExecute(), 可在自定义线程池时实现这些方法

思考

ctl变量怎么做到获取线程池状态和工作线程数量两个值的维护?

-- 5、用Future获取线程池执行结果

1、案例分析:用Future获取线程池执行的结果

2、原理分析:Future的实现原理

Future案例

模拟携程查询各航空公司机票信息服务,获取余票及价格结果

任务队列控制时间

核心原理

Future总结

原理

主线程阻塞等待线程池执行结果,从共享变量中获取结果

适用场景

需要程序提高执行速度,并且需要获取执行结果

需要有超时等待任务执行完毕,超时获取结果

不适用场景

只需要提交一个任务(不需要超时等待结果),且主线程提交任务后没有其他事情可做,提交后马上等待任务结果

还有哪些方式?

Thread.join()

CountDownLatch

……

思考

如何只获取3秒内所有已经返回结果的机票信息?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

plenilune-望月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值