多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] # 线程池的实现原理 ## 4种实现线程池的方式 有4种实现线程池的方式`newFixedThreadPool`、`newCachedThreadPoo`l、`newSingleThreadExecutor`、`newScheduledThreadPool`; `newFixedThreadPool`:创建一个指定线程数的线程池,当corePoolSize == maximumPoolSize时,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。 `newCachedThreadPool`:初始化一个可以缓存线程的线程池,使用SynchronousQueue作为阻塞队列,当线程的空闲时间超过keepAliveTime,会自动释放线程资源, 当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;需控制并发任务数,否则会创建大量的线程 `newSingleThreadExecutor`:初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。 `newScheduledThreadPool`:初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。 前三种实现方式 new TreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler); corePoolSize:线程池中核心线程数; maximumPoolSize:线程池中允许的最大线程数; keepAliveTime:线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间; TimeUnit: keepAliveTime的单位; workQueue:阻塞队列,有四种结构的阻塞队列 (1)ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务; (2)LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene; (3)SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene; (4)priorityBlockingQuene:具有优先级的无界阻塞队列; ## threadFactory:创建线程的工厂 handler:线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略,也可以自定义饱和策略,如记录日志或持久化存储不能处理的任务 (1)AbortPolicy:直接抛出异常,默认策略; (2)CallerRunsPolicy:用调用者所在的线程来执行任务; (3)DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务; (4)DiscardPolicy:直接丢弃任务 任务提交:两种提交方式Executor.execute()、ExecutorService.submit() `void Executor.execute()`:必须实现Runnable接口,没有返回值,无法判断任务是否执行成功; `<T> Future<T> ExecutorService.submit()`:可以获取任务执行完的返回值。 ## Executor.execute()线程池处理任务过程: (1)获取线程池中当前线程池数量,如果当前线程池数量小于coolPoolSize,则创建新的线程执行任务; (2)如果线程池处于running状态,并且提交的任务成功放到了阻塞队列中,则执行步骤(3),否则执行步骤(4); (3)再次检查线程池的状态,如果线程池状态不是running,并且成功将任务移除阻塞队列,则执行reject方法处理任务 (4)创建新的线程执行任务,如果执行失败,执行reject方法处理任务 ## 线程池是怎样实现创建新线程并执行任务的: (1)判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回; (2)判断当前需要创建的线程是否是核心线程,如果是核心线程,且线程数小于coolPoolSize,则开始创建新线程; (3)开始创建线程:线程池的工作线程通过Woker类实现,在ReentrantLock锁的保证下,把Woker实例插入到HashSet后,并调用start()方法启动Woker中的线程,执行runWorker方法。 (4)runWorker方法是线程池的核心: a.通过unlock方法释放锁; b.获取第一个任务firstTask,执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁; c.firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果阻塞队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源; d.当队列中有任务加入时,线程被唤醒,并返回任务去执行,如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null; ## ExecutorService.submit()线程池处理任务过程: (1)在实际业务场景中,Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果 ``` private ExecuteService executor = Executors.newFixedThreadPool(10); public static void main(String[] args){ Future<String> future = executor.submit(new Task()); String result = future.get(); } static class Task implements Callable<String>{ @override public String call(){ return "success"; } } ``` (2)Callable接口类似于Runnable,只是Runnable没有返回值。 (3)Callable任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即Future可以拿到异步执行任务各种结果; (4)Future.get方法会导致主线程阻塞,直到Callable任务执行完成;