什么线程池?
- 我们都知道线程的开辟和销毁都需要一定的成本,线程池提高了线程的利用率,会复用一部分线程来减少开辟销毁的成本
java 中如何创建线程池?
-
我们可以使用Executors 包创建线程池
ExecutorService service = Executors.newCachedThreadPool(); //创建可缓存的线程池 ExecutorService service1 = Executors.newFixedThreadPool(10);//可重用固定线程数的线程池,使用LinkedBlockingQueue ExecutorService service2 = Executors.newSingleThreadExecutor();//只有一个线程的线程池 使用LinkedBlockingQueue ExecutorService service3 = Executors.newScheduledThreadPool(3);//主要用来在给定的延迟后运行任务,或者定期执行任务
-
实际项目中我们不推荐使用Executors ,调用线程池执行时,当要执行的线程数大于核心线程数量 , 线程会进入阻塞队列等待执行,而LinkedBlockingQueue 是一个无边阻塞队列,线程不会满足拒绝策略条件,从而导致oom发生
-
正确的操作 使用ThreadPoolExecutor 创建
ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
)
- 我们可以选择用ArrayBlockingQueue 作为阻塞队列,这有我们就可以限制线程长度了
- 这又衍生出了一个新的问题,如何阻塞队列满了怎么办?
- ThreadPoolExecutor 中 提供了一个拒绝策略的机制,我们可以在创建线程池时指定拒绝策略,默认为 new AbortPolicy();
线程池解决策略
-
触发时机
- 当阻塞队列满了或者调用了shutdown ()方法之后,会触发线程时拒绝策略
-
几种拒绝策略
- AbortPolicy 该策略会抛出RejectedExecutionException 的 RuntimeException 异常
- DiscardPolicy 比较狗,直接丢弃,你也不知道
- DiscardoldestPolicy 丢失阻塞队列的一个线程
- CallerRunsPolicy 在调用者的线程执行
线程池的合理参数
- 抱着多一个核心浪费的心态 分为一下两种情况
- cpu密集时 使用 n+1 (n 为cpu核心)
- io密集时 使用2n+1