- 为什么要用线程池呢?
虽然创建线程看起来很简单,只需要new Thread()就可以了,但实际上创建线程远不止创建一个对象这么简单。创建对象,仅仅是在堆内存中分配一块内存而已,而创建线程则需要调用操作系统内核API,然后操作系统要为线程分配一系列的资源,这个成本比创建一个对象时要高很多的。所以线程是一个重量级的对象,应避免频繁创建和销毁。
- 线程池几个核心参数
- corePoolSize:线程池保有的最小线程数。即使整个线程池都很很闲,没有任务可执行,也需要保留corePoolSize线程.
- maximumPoolSize:线程池创建的最大线程数。当任务很多时,就需要增加线程来处理了,最多增加到maximunPoolSize个线程。当任务减少线程空下来时,会回收线程,保留corePoolSize个线程。
- KeepAliveTime和unit:如何定义线程是否空闲呢?如果一个线程在一段时间内都未执行任务,则认为是空闲的,而KeepAliveTime和unit就是用来定义"一段时间"的。keepAliveTime是时间,unit是时间的类型。如果一个线程空闲了KeepAliveTime和unit这个久,而且线程池内的线程数大于corePoolSize,那么这个空闲线程将会被回收。
- workQueue:工作队列。当线程数达到corePoolSize数量时,再有任务提交,会被放入内存队列中,如果该队列是有界队列,当该队列满了时,会继续创建不超过maxmumPoolSize线程数的线程。当有线程执行完任务会从该队列中取任务进行执行。使用无界队列会有OOM的风险。
- threadFactory: 通过这个参数可以自定义如何创建线程,例如:你可以个线程指定一个有意义的名字。
- handler:拒绝策略。通过该参数可以自定义任务的拒绝策略。如果线程池内的线程都在忙碌,并且workQueue队列也已经满了(前提队列是有界队列),此时提交线程线程池就会执行拒绝策略,至于拒绝的策略是什么,可以通过handler这个参数来指定。
- ThreadPoolExecutor所提供的4种拒绝策略。
- CallerRunsPolicy: 提交任务的线程自己去执行该任务。
- AbortPolicy: 默认的决绝策略,会thows RejectedExecutionException。
- DiscardPolicy: 直接丢弃任务,没有任何异常抛出。
- DiscardOldestPolicy: 丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。
- 不建议使用Executors创建线程池的几点
- Executors提供的方法默认使用的都是无解队列的LinkedBlockingQueue,高负载的情况下很容易导致OOM,OOM会导致所有请求无法处理。强烈建议使用有界队列。
- 使用有界队列,当任务数过多时,会触发默认的拒绝策略,线程池默认拒绝策略会抛出throw RejectedExecutionException 这是个运行时异常,运行时异常编译器并不强制catch,开发人员容易忽略。。采用默认拒绝策略要慎重。 如果线程池处理的任务特别重要,可以自定义拒绝策略和降级策略一起使用。
- 要注意异常处理问题。例如通过ThreadPoolExecutor对象的execte()方法提交任务时,如果任务执行过程中出现异常,会导致执行任务的线程终止,最致命的是任务虽然异常了,但你却获取不到任何通知,会让你误以为任务都执行的很正常。虽然线程池提供了很多用于异常处理的方法,但是最稳妥和简单的方案还是捕获所有异常并按需处理