ThreadPoolExecutor基础使用

本文介绍了Java的ThreadPoolExecutor线程池的使用,包括其在Android开发中的重要性,核心参数配置,线程状态管理,以及正常和异常执行任务的测试。通过实例展示了线程池如何处理任务队列、线程创建、任务拒绝策略和异常处理,帮助理解线程池的工作原理。
摘要由CSDN通过智能技术生成

前言

Android开发中由于禁止在主线程中做网络请求,通常都需要使用线程对象来做异步请求操作,但是直接使用new Thread();创建新线程需要不停的申请系统资源,这些野生的线程缺乏统一管理,相互竞争占用过多系统资源;直接使用普通的线程做定期执行和线程中断等功能特别容易出错,为此 J.U.C类库里添加了功能强大的Executor框架,它能够为用户程序提供强大的线程池实现。使用JDK自带的线程池功能有如下的优点:

  1. 可以重用已有的线程,减少对象创建和销毁;
  2. 可以被有效控制最大并发线程数,提高系统资源利用率,避免过多资源竞争;
  3. 提供定时执行定期执行单线程和并发数据控制等功能

基础介绍

J.U.C的Excutor框架最主要的子类就是ThreadPoolExecutor,它是大部分线程池的底层实现对象,通常只需要学会如何配置它的运行参数就能够产生功能相当强大的线程池实现。它的构造函数主要包含如下几个参数:

参数名 作用
corePoolSize 核心线程数量
maximumPoolSize 线程最大线程数
workQueue 阻塞队列,存储等待执行的任务会对线程池线程运行过程产生重大影响
keepAliveTime 线程没有任务执行是最多保持多久时间终止
unit 时间单位
threadFactory 线程工厂,用来创建线程
rejectedExcutionHandler 如果提交的任务太多无法被处理,需要执行的拒绝策略

线程池合理配置,需要尽量压榨CPU,CPU密集型任务核心线程数参考值可以设置为NCPU+1,IO密集型任务参考值可以设置为2*NCPU

线程池内部使用状态机管理状态,主要分成运行、关闭、停止、清理、终止五种状态,它们之间的相互转换如下:
这里写图片描述
在关闭状态之前提交的任务会被继续执行完成,但新添加的任务会被抛弃,关闭状态会直接停止所有线程和正在执行的任务,清理完了任务和工作线程之后进入清理状态,最后完成终止回调进入终止状态,线程池就不能再使用了。

普通的Thread请求无法返回结果,而很多网络请求就是为了从服务器获取数据,直接使用Runnable就需要使用共享变量来接收返回结果,这种跨线程的数据请求很容易出现问题。Executor框架为用户提供了FutureTask类,它实现了RunnableFuture接口,而RunnableFuture实现了Runnable和Future两个接口,Future代表一个执行结果对象,调用它的get/cancel/isDown方法能够获取、取消和查看执行结果,传递进线程池的任务通常都会被封装成FutureTask对象来执行。

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

上面的代码就是AbstractExecutorService几个需要返回结果的执行接口,它们都会把Runnable或者Callable对象封装到RunnableFuture对象中去,execute接口不会做任何封装,它只接受Runnable类型的任务对象。

正常执行测试

前面已经了解过线程池对象的几个参数,现在使用自定义生成的参数来配置一个线程池,测试它在不同情况下的执行效果。首先为线程池添加自定义的ThreadFactory对象,这个线程工厂每次产生新线程的时候都会打印创建线程的名字。接着定义当任务过多线程池无法接受过多任务会执行拒绝策略,这里只是简单的打印出被拒绝的任务。

ThreadFactory threadFactory = new ThreadFactory() {
    private AtomicLong mAtomicLong = new AtomicLong(0);

    @Override
    public Thread newThread(@NotNull Runnable r) {
        Thread thread = new Thread(r, "My-Thread-" + mAtomicLong.getAndIncrement());
        // 创建新线程的时候打印新线程名字
        System.out.println("Create new Thread(): " + thread.getName());
        return thread;
    }
};

RejectedExecutionHandler handler = new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        // 打印被拒绝的任务
        System.out.println("rejectedExecution" + r.toString());
    }
};

接着来为线程池配置任务队列,也就是在核心线程已经全部创建而且都在执行任务,这时又有新的任务被提交它们就会被放置在任务队列中,任务队列可以分为有界队列和无界队列。

// 有界队列,最多能存放5个任务对象
BlockingQueue<Runnable> limitArray = new ArrayBlockingQueue<>(5);
BlockingQueue<Runnable> limitlist = new LinkedBlockingQueue<>(5);

// 无界队列可以存放Integer.MAX_VALUE个任务
BlockingQueue<Runnable> unlimitList &#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值