栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java中的线程池回顾总结

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中的线程池回顾总结

目录

线程池的优势线程池的状态线程池的创建线程池的种类线程池提交任务流程线程池提交任务的方式(`API`)

`Runnable` 接口与 `Callable` 接口`ThreadPoolExecutor` 类中提交任务的方法

线程池的优势

降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁所造成的系统资源消耗提高系统响应速度,当有任务到达时,通过复用已存在的线程,任务可以不需要等待新线程的创建便能立即执行提高线程的可管理性,方便线程并发数的管控。如果线程无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,可能也会导致内存占用过多而产生 OOM提供更强大的功能,延时定时线程池 线程池的状态

ThreadPoolExecutor 类中使用 int 的高 3 位来表示线程池状态

状态value说明
RUNNING(当线程池创建出来的初始状态)111能接受任务,能执行阻塞任务,能执行正在执行的任务
SHUTDOWN(调用shutdown方法)000不接受新任务,能执行阻塞任务 ,能执行正在执行的任务
STOP(调用shutDownNow)001不接受新任务,打断正在执行的任务,丢弃阻塞任务
TIDYING(中间状态)010任务全部执行完,活动线程也没了
TERMINATED(终结状态)011线程池终结

这几种状态的转换过程

线程池在构造前(new 操作)是初始状态,一旦构造完成线程池就进入了执行状态 RUNNING;严格意义上讲线程池构造完成后并没有线程被立即启动,只有进行“预启动”或者接收到任务的时候才会启动线程。但是线程池是出于运行状态,随时准备接受任务来执行线程池运行中可以通过 shutdown() 和 shutdownNow() 方法来改变运行状态;线程池 Executor 是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行

shutdown() 是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于 SHUTDOWN 状态shutdownNow() 是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于 STOP 状态 一般情况下我们认为 shutdown() 或者 shutdownNow() 执行完毕,线程池就进入 TERMINATED 状态,此时线程池就结束了。其实,在 SHUTDOWN 或 STOP 到 TERMINATED 状态之间还存在一个 TIDYING 状态当任务队列和线程池均为空的时候,线程池的状态由 STOP 或 SHUTDOWN 状态进入到 TIDYING 状态当 terminated() 方法被调用完成之后,线程池的状态由 TIDYING 进入到 TERMINATED 状态 线程池的创建

阿里巴巴的 Java 开发手册规定:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险;Executors 返回的线程池对象的弊端如下

FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOMCachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,// 时间单位 
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

corePoolSize:核心线程数不能小于 0;如果此时线程池的线程数量小于核心线程数,那么线程池会新创建一个线程来执行任务,即使此时存在空闲线程也不例外

maximumPoolSize:最大线程数不能小于 corePoolSize;该参数会根据使用的 workQueue 任务队列的类型,决定线程池会开辟的最大线程数量

keepAliveTime:超过 corePoolSize 的线程的空闲时长,超过这个时间,多余的线程会被回收;空闲线程等待超时时间不能小于 0

workQueue:当线程任务添加的速度超过所有核心线程执行速度时,新来的来不及执行的线程任务将被存放到 workQueue 阻塞任务队列中

ArrayBlockingQueue:有界阻塞任务队列linkedBlockingQueue:通常作为无界阻塞任务队列,当有大量任务提交时,容易造成内存耗尽SynchronousQueue:一个没有容量的阻塞队列,会将任务同步交付给工作线程PriorityBlockingQueue:具有优先级的无界阻塞任务队列

threadFactory:线程工厂,用于创建线程,一般用默认即可

handler:拒绝策略,当任务太多来不及处理时,如何拒绝任务

AbortPolicy:丢弃任务并抛出异常,作为默认拒绝策略CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务DiscardPolicy:丢弃任务,但是不抛出异常 线程池的种类

newFixedThreadPool:创建一个固定大小的线程池newCachedThreadPool:创建一个可缓存的线程池newScheduledThreadPool:创建一个支持定时及周期性任务执行的线程池newSingleThreadExecutor:创建一个单线程的线程池 线程池提交任务流程

提交任务流程源码分析:execute() 核心提交任务方法

如果此时线程池中的线程数量 < corePoolSize 时,即 corePoolSize 核心线程池未满,则创建新的线程来处理被添加的任务(即使线程池中的线程都处于空闲状态)如果此时线程池中的线程数量 >= corePoolSize 时,即 corePoolSize 核心线程池已满,会再去判断 workQueue 队列是否已满(队列添加任务成功,说明未满;反之,说明队列已满),如果队列未满,则将任务放入到 workQueue 队列中等待执行当 workQueue 队列已满时,如果此时线程池中的线程数量 < maximumPoolSize 时,即该线程池未满,则继续创建线程来执行任务如果此时线程池中的线程数量 > maximumPoolSize 时,即该线程池已满,则通过 handler 所指定的拒绝策略来处理此任务 线程池提交任务的方式(API) Runnable 接口与 Callable 接口

可以向线程池提交的任务有 Runnable 和 Callable 两种,其中,Callable 是 jdk 1.5 时加入的接口,作为 Runnable 的一种补充,允许有返回值,允许抛出异常

@FunctionalInterface
public interface Callable {
	
    V call() throws Exception;
}

这里延伸一下使用 Callable 创建多线程

public class CallableFutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println(Thread.currentThread().getName());
                return 200;
            }
        });
		
		// FutureTask 类实现了 Runnable 接口,用 Runnable 创建一个线程
        // 注意这里,这里传入 futureTask 参数,构造了一个 Thread
        new Thread(futureTask).start();
        int result = (int) futureTask.get(); // 获取线程的最终结果
        System.out.println("result的结果:" + result);
        System.out.println("main come over");
    }
}

FutureTask 类实现了 RunnableFuture 接口,RunnableFuture 继承了 Runnable 与 Future 接口,因此 FutureTask 既可以作为一个 Runnable 被 Thread 执行,也可以获取到 Future 异步计算的结果Futrue、Callable、Runnable、FutureTask 的关系:详情查看这里 ThreadPoolExecutor 类中提交任务的方法

void execute(Runnable command)Future submit(Callable task)Future submit(Runnable task) :虽然返回 Future,但是其 get() 方法总是返回 nullFuture submit(Runnable task, T result)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/716624.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号