如何优雅的使用线程池!!! 您所在的位置:网站首页 如何优雅地使用幕布视频讲解 如何优雅的使用线程池!!!

如何优雅的使用线程池!!!

2024-06-17 02:38| 来源: 网络整理| 查看: 265

线程池

在前面使用的例子用,我们已经使用过线程池,基本上就是初始化线程池实例之后,把任务丢进去,等待调度执行就可以了,使用起来非常简单、方便。虽然使用很简单,但线程池涉及到的知识点非常多。需要分析其实现。

JAVA中Thread这个类是线程类,在JAVA基础时,对于线程的认识是基于此类,为什么不使用Thread直接执行线程例子呢,而要使用线程池?可以试想,当并发数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。而线程池可以达到这样的效果:线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。

Thread的弊端:

每次 new Thread() 新建对象,性能差;线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或OOM;缺少更多的功能,如更多执行、定期执行、线程中断;

线程池的好处

重用存在的线程,减少对象创建、消亡的开销,性能佳,降低资源消耗;可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞,提高响应速度;提供定时执行、定期执行、单线程、并发数控制等功能,以达到提高线程的可管理性。

阿里发布的 Java 开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

Executors利用工厂模式向我们提供了4种线程池实现方式,但是并不推荐使用,原因是使用Executors创建线程池不会传入相关参数而使用默认值所以我们常常忽略了那些重要的参数(线程池大小、缓冲队列的类型等),而且默认使用的参数会导致资源浪费,不可取。

ThreadPoolExecutorConstructor And Parameters

java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类,因此我们直接上源码:

代码语言:javascript复制public class ThreadPoolExecutor extends AbstractExecutorService { /** 构造函数 1 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {} /** 构造函数 2 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {} /** 构造函数 3 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {} /** 构造函数 4 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {} }

ThreadPoolExecutor 类继承结构是:Executor(I) maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } /** ------------------------以上的操作跟之前类似----------------------- */ /** ------------------------关键在于下面的代码------------------------- */ /** ------------------------从阻塞队列中获取任务----------------------- */ try { Runnable r = timed ? //对于阻塞队列,poll(long timeout, TimeUnit unit) 将会在规定的时间内去任务 //如果没取到就返回null workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //take会一直阻塞,等待任务的添加 workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }

到此,终于发现为什么线程池能够保证一直等待任务而不被销毁,其实就是进入了阻塞状态。

ThreadPoolExecutor.processWorkerExit()代码语言:javascript复制/** * @param completedAbruptly 工作线程是否死与执行任务出现的异常 */ private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) //如果突然被打断,工作线程数不会被减少 decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); } finally { mainLock.unlock(); } tryTerminate(); int c = ctl.get(); //判断运行状态是否在STOP之前 if (runStateLessThan(c, STOP)) { if (!completedAbruptly) {//正常退出,也就是task == null int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } //新增一个工作线程,代替原来的工作线程 addWorker(null, false); } } AbstractExecutorService.submit()

向线程池中提交一个需要返回结果的任务

代码语言:javascript复制public Future submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task, null); execute(ftask); return ftask; } public Future submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task, result); execute(ftask); return ftask; } public Future submit(Callable task) { if (task == null) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task); execute(ftask); return ftask; }

在ThreadPoolExecutor中并未发现submit(),因此从父类,即抽象类AbstractExecutorService中找到submit()的方法实现,从方法实现中,可知:

submit()接收任务参数,并将参数封装为FutureTask任务类将封装好的FutureTask提交到execute()中

结论:submit()真正实现的任务处理流程跟execute()一样,也可以说submit()就是调用了execute()

从上面的流程图可以知道,向线程池提交一个任务后,共经历以下流程:

提交任务到线程池;线程池判断核心线程池里是的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下一个流程。线程池判断工作队列是否已满。如果工作队列没有满,则将新提交的任务储存在这个工作队列里。如果工作队列满了,则进入下一个流程。线程池判断其内部线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已满了,则交给饱和策略来处理这个任务。ThreadPoolExecutor.shutdown()代码语言:javascript复制public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess();//检查终止线程池的线程是否有权限。 advanceRunState(SHUTDOWN);// 设置线程池的状态为关闭状态。 interruptIdleWorkers();// 中断线程池中空闲的线程 onShutdown(); // 钩子函数,在ThreadPoolExecutor中没有任何动作 } finally { mainLock.unlock(); } tryTerminate();// 尝试终止线程池 }

作者:chuIllusions丶

来源:jianshu.com/p/5b692c96e08d



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有