JUC----线程池(二)

前言

上一篇文章我们了解了系统中关于线程池的父类ThreadPoolExecutor以及它的相关参数,我们今天来谈一谈关于JDK中给我们预定义的线程池。
JUC----线程池(一)

1. 阻塞队列

了解预定义线程池之前我们先来了解一个阻塞队列的概念,它是线程池中存储任务的容器。

1.1 常用方法(线程池中一般使用一直阻塞的方法)

在这里插入图片描述

1.2 常用的阻塞队列
  • ArrayBlockingQueue: 一个由数组结构组成的有界阻塞队列。(按照先进先出的原则,要求设定初始大小)
  • LinkedBlockingQueue: 一个由链表结构组成的无界阻塞队列(按照先进行先出的原则,可以不设定初始大小,默认为Integer.Max_Value)

两者的区别:
锁上面:ArrayBlockingQueue只有一个锁,LinkedBlockingQueue用了两个锁
实现上:ArrayBlockingQueue直接插入元素,LinkedBlockingQueue需要转换

  • SynchrousQueue: 一个不存储元素的阻塞队列 (每一个put操作要等待一个take操作)
  • PriorityBlockingQueue: 一个支持优先排序的无界阻塞队列(默认情况下,按自然顺序排序,要么实现compareTo() 方法,要么指定构造参数Compartor)
  • LinkedTransferQueue: 一个由链表机构组成的无界阻塞队列

transfer() : 必须要消费者消费了以后才会返回
tyrTransfer(): 无论消费者是否接收。方法都立即返回

  • LinkedBlockingDeque: 一个由链表组成的双向阻塞队列 (可以在队列的头部和尾部插入,移除元素<这一点上它能实现工作密取>,其中方法名带了First从头部开始操作,带了last尾部开始操作)

add (默认) addLast,
remove(默认) removeFirst,
take(默认) takeFirst

2. 系统中预定义的线程池

(以下new出线程池的方法都出自Executors类,我们将对它的来龙去脉做个详解。)

2.1 FixeedThreadPool (固定数量的线程池)

创建固定线程数量的,适用于负载量适中的服务。它使用的是无界阻塞队列。
(0L表示某一个时刻可能会超出线程的数量,这是线程会马上停止下来)
在这里插入图片描述

2.2 SingleThreadExecutor(单一线程数量的线程池)

创建单个线程。需要顺序保证执行任务,不会有多个线程的活动,使用无界阻塞队列。
在这里插入图片描述

2.3 CachedThreadPool

会根据需求来创建新线程,执行很多短期异步任务的程序,使用了SynchronousQueue(一个不存储元素的阻塞队列)
(存在问题:当我们提交的任务数量远大于线程的数量的时候,它会不断地创建新线程。那么服务最终会垮掉。)
在这里插入图片描述

2.4 WorkStealingPool (JDK1.7后才推出的)

它是唯一一个基于ForkJoinPool(一种基于分而治之实现的线程池)
在这里插入图片描述

2.5 ScheduledExecutor(定时任务

需要定期执行周期任务,在多线程环境下不建议使用Timer.
相关构造方法:
newSingleThreadScheduledExecutor(): 只包含一个线程,只需要单个线程执行周期任务,保证顺序地执行各个任务。
在这里插入图片描述
newScheduledThreadPool() :可以包含多个线程的,线程执行周期任务,可以适度控制后台线程的数量
在这里插入图片描述
相关方法说明:

schedule: 只执行一次,任务还可以延时执行
scheduleAtFixedRate:提交固定时间间隔的任务
scheduleWithFixedDelay:提交固定延时间隔的任务

关于时间间隔和延时间隔的区别:
在这里插入图片描述
时间间隔:从一个任务的开始到下一个任务的开始
延时间隔:从一个任务的结束到下一个任务的开始

相关细节说明:
scheduleAtFixedRate 的任务超时的时候(固定时间间隔)
假设规定60s执行一次,有任务执行了80s, 那么下个任务会在这个延时任务执行完后马上执行
如:第一个任务执行时长为80s, 第二个任务为20s,第三任务为50s(假设中间没有间隔)
第一个任务从0s开始,80s结束。
第二个任务从80s开始,100s 结束
第三个任务从120s 开始,170s 结束
第四个任务从180s 开始
(注意:建议在提交给ScheduledExecutor的任务要捕捉异常)
scheduleWithFixedDelay固定延时就没有这样的问题,因为它的间隔不会经过任务的执行长度(这样不管任务的长短都不会影响)

3. Executor框架

在平时的使用中,我们经常会遇到ExecutorServices, ThreadPoolExecutor, ScheduledExecutor ;其实他们之间存在一个很重要的联系。
(以下内容参考自:Java并发——Executor框架详解(Executor框架结构与框架成员)

3.1 Executor框架是什么?

从JDK1.5开始,为了把工作单元与执行机制分离开,Executor框架诞生了,他是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。

3.2 Executor框架由什么组成

[1] 任务: 也就是工作单元,包括执行任务需要实现的接口:Runnable接口或者Callable接口
[2] 任务的执行: 也就是把任务分派给多个线程的执行机制,包括Executor接口,及继承自Executor接口的ExecutorService接口(该接口下就是关于线程池的类)。
[3] 异步计算的结果:包括Future接口以及实现了Future接口的FutureTask类。
在这里插入图片描述

  • 从下图我们可以观察得知,我们线程池的创建最终都要返回一个ExecutorService, 因为这些JDK为我们预定义的线程池底层是通过new ThreadPoolExecutor()实现的,而ThreadPoolExecutor又是间接继承自ExecutorService的,那么这也就不足为其了。
    在这里插入图片描述
    在这里插入图片描述
  • 而对于ScheduledExecutor(定时任务),它的返回值与其他线程池不同,是ScheduledExecutorService,可以看到它也是继承自ExecutorService接口的
    在这里插入图片描述
3.3 Executor框架成员

ThreadPoolExecutor实现类(线程池的创建)、ScheduledThreadPoolExecutor实现类(定时任务的线程池)、Future接口(当我们执行任务需要返回结果时)、Runnable和Callable接口(线程创建的方式)、Executors工厂类
Executors工厂类:提供了常见配置线程池的方法,因为ThreadPoolExecutor的参数众多且意义重大,为了避免配置出错,才有了Executors工厂类。
在这里插入图片描述

4. 定时任务(ScheduledThreadPoolExecutor的使用)
4.1 定时任务类 ScheduleWorker

执行任务时,我们一般有三种类型;一种是执行普通任务正常返回;一种是会抛出异常的任务类型;一种是抛出异常但是会捕捉的任务类型。
下面的任务类主要针对这三种执行任务的类型书写的定时任务的工作类(Runnable)

package 定时任务;

import java.text.SimpleDateFormat;
import java.util.Date;

/*类说明:定时任务的工作类*/
public class ScheduleWorker implements Runnable {

    public final static int Normal = 0; //普通任务类型
    public final static int HasException = -1; //会抛出异常的任务类型
    public final static int ProcessException = 1; //抛出异常但会捕捉的任务类型

    public static SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private int taskType;

    public ScheduleWorker(int taskType) {
        this.taskType = taskType;
    }

    @Override
    public void run() {
        if(taskType == HasException){
            System.out.println(formater.format(new Date())+" Exception made...");
            throw new RuntimeException(" HasException Happen");
        }else if(taskType == ProcessException){
            try{
                System.out.println(formater.format(new Date())+
                        " Exception made, but catch...");
            }catch (Exception e){
                System.out.println(" Exception is be catched");
            }
        }else {
            System.out.println(" Normal...."+formater.format(new Date()));
        }
    }
}

4.2 使用ScheduledThreadPoolExecutor生成相关的线程池进行任务的执行

【1】执行正常返回任务的时候 (ScheduleWorker.Normal)

public class ScheduledCase {
    public static void main(String[] args) {
        ScheduledThreadPoolExecutor schedule =
                new ScheduledThreadPoolExecutor(1);
        schedule.scheduleAtFixedRate(new ScheduleWorker(ScheduleWorker.Normal),
                            1000,3000, TimeUnit.MILLISECONDS);
    }
}

控制台每隔3s会打印一次
在这里插入图片描述
【2】执行有异常且不捕捉的任务(ScheduleWorker.HasException)
建议:使用捕获异常的任务类
在这里插入图片描述
【3】执行有异常且捕获的任务类(ScheduleWorker.ProcessException)
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值