Loading... 出现原因? 在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时,为每一个任务创建一个新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。 Java的线程既是工作单元,也是执行机制。从JDK 5开始,把工作单元与执行机制分离开来。工作单元包括Runnable和Callable,而执行机制由Executor框架提供。 总体框架: <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-065048.png" alt="image-20230424145044895" style="zoom:50%;" style=""> ## 框架结构 **Executor框架主要由3大部分组成如下。** ·任务。包括被执行任务需要实现的接口:Runnable接口或Callable接口。 ·任务的执行。包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架有两个关键类实现了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。 ·异步计算的结果。包括接口Future和实现Future接口的FutureTask类。 接口介绍: ·Executor是一个接口,它是Executor框架的基础,它将任务的提交与任务的执行分离开来。 ·ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务。 ·ScheduledThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。ScheduledThreadPoolExecutor比Timer更灵活,功能更强大。 ·Future接口和实现Future接口的FutureTask类,代表异步计算的结果。 ·Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或Scheduled-ThreadPoolExecutor执行。 <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-065355.png" alt="image-20230424145352275" style="zoom:50%;" style=""> <font color='red'><b> 主线程首先要创建实现Runnable或者Callable接口的任务对象。工具类Executors可以把一个Runnable对象封装为一个Callable对象(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。</font> <font color='red'><b> 然后可以把Runnable对象直接交给ExecutorService执行(ExecutorService.execute(Runnable command));或者也可以把Runnable对象或Callable对象提交给ExecutorService执行(Executor-Service.submit(Runnable task)或ExecutorService.submit(Callable<T>task))。</b></font> <font color='red'><b> 如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象(到目前为止的JDK中,返回的是FutureTask对象)。由于FutureTask实现了Runnable,程序员也可以创建FutureTask,然后直接交给ExecutorService执行。</b></font> <font color='red'><b> 最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。</b></font> ## 框架成员 ### ThreadPoolExecutor 通常使用工厂类Executors来创建。Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadPool和CachedThreadPool。 FixedThreadPool创建使用固定线程数的线程池。**适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器。** SingleThreadExecutor单个线程的线程池,**适用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。** CachedThreadPool**是大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。** ### ScheduledThreadPoolExecutor ·ScheduledThreadPoolExecutor。包含若干个线程的ScheduledThreadPoolExecutor。 ·SingleThreadScheduledExecutor。只包含一个线程的ScheduledThreadPoolExecutor。 **ScheduledThreadPoolExecutor适用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景。** **SingleThreadScheduledExecutor适用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景。** ### Future接口 Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。当我们把Runnable接口或Callable接口的实现类提交(submit)给ThreadPoolExecutor或ScheduledThreadPoolExecutor时,ThreadPoolExecutor或ScheduledThreadPoolExecutor会向我们返回一个FutureTask对象。 ### Runable和Callable接口 Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或Scheduled-ThreadPoolExecutor执行。它们之间的区别是Runnable不会返回结果,而Callable可以返回结果。 ## 框架组件 ·corePool:核心线程池的大小。 ·maximumPool:最大线程池的大小。 ·BlockingQueue:用来暂时保存任务的工作队列。 ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。 #### FixedThreadPool <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-071526.png" alt="image-20230424151520918" style="zoom:50%;" style=""> #### SignalThreadPool <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-071547.png" alt="image-20230424151542955" style="zoom:50%;" style=""> #### CachedThreadPool <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-071650.png" alt="image-20230424151641872" style="zoom:50%;" style=""> #### ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改。 ·使用DelayQueue作为任务队列。 ·获取任务的方式不同(后文会说明)。 ·执行周期任务后,增加了额外的处理(后文会说明)。 <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-071915.png" alt="image-20230424151912630" style="zoom:50%;" style=""> ##### 实现 <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-072045.png" alt="image-20230424152041962" style="zoom:50%;" style=""> 1)线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前时间。 2)线程1执行这个ScheduledFutureTask。 3)线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。 4)线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(Delay-Queue.add())。 .... ### FutureTask 根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态。 1)未启动。FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态。当创建一个FutureTask,且没有执行FutureTask.run()方法之前,这个FutureTask处于未启动状态。 2)已启动。FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。 3)已完成。FutureTask.run()方法执行完后正常结束,或被取消(FutureTask.cancel(…)),或执行FutureTask.run()方法时抛出异常而异常结束,FutureTask处于已完成状态。 <img src="https://sydblog-1999-1256393142.cos.ap-nanjing.myqcloud.com/blog/2023-04-24-072220.png" alt="image-20230424152216910" style="zoom:50%;" style=""> #### FutureTask使用 ... #### FutureTask实现 ... 最后修改:2023 年 04 月 24 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏