为什么使用多线程
- 降低资源消耗 -事先创建若干个线程放在容器中,当使用的时候不需要自行创建,使用完不是去销毁而是归还到容器,减少了线程创建和销毁的时间
- 提高线程的可管理性 -无限制的创建线程,不仅消耗资源还降低系统稳定性,线程池可以进行统一分配,监控
- 提高相应速度 -不需要等待线程创建
参数说明
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable workQueue)
corePoolSize(最大核心线程数): 线程池启动后,在池中保持的线程的最小数量
maxinumPoolSize(线程池能容纳的最大线程数量): 核心线程数 + 非核心线程数 = 最大线程数量
unit: keepAliveTime的时间单位,可以是纳秒,毫秒,秒,分钟等
keepAliveTime: 线程的最大生命周期
workQueue: 任务队列
threadFactory: 定义如何启动一个线程,可以设置线程的名称,并且可以确定是否是后台线程等。new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
RejectedExecutionHandler: 拒绝任务处理器。由于超出线程数量和队列容量而对继续增加的任务进行处理的程序。
workQueue:
ArrayBlockingQueue:基于数组结构的有界队列,先进先出(FIFO)原则对元素排序
LinkedBlockingQueue:基于链表结构的阻塞队列,先进先出排序元素,吞吐量高于数组结构。
Executors.newFixedThreadPool()
使用这个队列SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等到下一个线程调用一处操作,否则插入操作一直处于阻塞状态,吞吐量高于2,
Executors.newCachedThreadPool()
使用这个队列- PriorityBlockQueue:具有优先级的无线阻塞队列
DelayQueue(延时队列):任务到来时,首先先加入到队列中,只有达到了指定的延时时间,才会执行任务
- #### RejectedExecutionHandler:
AbortPolicy(默认策略):直接拒绝抛异常(RejectedExecutionException)
CallerRunsPolicy:不抛弃任务,只有调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列最近的任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉,不抛异常
总结
- 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务。
- 线程数量达到了 corePoolsSize,则将任务移入队列等待。
- 队列已满,新建非核心线程(先进先出)执行任务。
- 队列已满,总线程数又达到了 maximumPoolSize,就会由 RejectedExecutionHandler 抛出异常。
阿里巴巴JAVA开发手册
【3】. 【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决
资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或
者“过度切换”的问题。
【4】. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
合理的配置线程池
可以从以下几个角度分析:
- 任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
- 任务的优先级:高、中、低。
- 任务的执行时间:长、中、短。
- 任务的依赖性:是否依赖其他系统资源,如数据库连接等。
- 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
可以得出一个结论:
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。 - CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务
- IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
- 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
- 建议使用有界队列:增加系统稳定性和预警能力。