多线程系列文章目录
文章目录
1. ThreadPoolExecutor的构造方法
java里线程池的本体是ThreadPoolExecutor,但是这个类的构造方法参数太多,写起来特别麻烦,为了简化使用构造,标准库里就提供了一些工厂方法来简化使用.比如:静态方法newCachedThreadPool()
ExecutorService pool = Executors.newCachedThreadPool();//调用的是静态的方法构造对象,工厂模式
//submit方法可以将要执行的任务提交到线程池里,然后线程池里的线程就会执行
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("执行任务");
}
});
这里主要是了解一下ThreadPoolExecutor的构造方法
1.corePoolSize和maximumPoolSize的区别:
corePoolSize是核心线程数,就是主要的线程数数目;
maximumPoolSize是最大线程数,是主要线程数+临时线程数;
如何理解两者区别?
就好比公司里的正式员工和实习生,当任务不多时,正式员工来处理业务,实习生可能就可以摸鱼打杂,但是当任务多时,光靠正式员工人数不够,还需要实习生也加入到业务处理当中,这里正式员工就相当于核心线程,实习生就相当于临时线程.
当任务不多时,可以去掉临时线程,合理的调配这两线程数量就可以做到既可以处理峰值,也可以在空闲时节省系统资源.
2.keepAliveTime和util
keepAliveTime就是允许实时线程摸鱼的最大时间,util是时间单位
3.BlockingQueueworkQueue
workQueue就是指手动给线程池传入一个任务队列,线程池内本来也有自己的任务队列(你不传队列引用的话,会自己创建一个队列),当业务逻辑需要一个队列来保存任务时,就直接将这个任务队列的引用传给线程池,而不是将自己的任务队列拷贝到线程池里的任务队列里.
4.ThreadFactory threadfactory
threadfactory这是一个工厂对象,描述了线程是如何创建的,程序员可以手动指定线程的创建策略.
5.[重要]RejectExecutionHandler handler
RejectExecutionHandler 是一个接口,RejectExecutionHandler handler需要创建一个ThreadPoolExecutor 对象,handler是线程池的拒绝策略,当线程池的任务队列满了(工作线程忙不过来了),如果又往里面加任务怎么办?来看RejectExecutionHandler里的拒绝策略:
AbortPolicy中止策略:当线程池已经有很多任务时,工作线程已经忙不过来了,在给线程池增加任务,程序直接抛异常终止,之前的任务和新增加的任务全部不在执行了.
CallerRunsPolicy分配者可执行策略:当工作线程A已经忙不过来时,其他线程B继续给工作线程分配任务,工作线程会拒绝执行这个执行这个任务,如果B可以执行这个任务就自己执行,如果B不可以执行这个任务就会将这个任务抛弃.
DiscardOldestPolicy丢弃旧任务策略:当工作线程已经忙不过来时,其他线程又给工作线程分配任务,此时,工作线程会先丢弃之前的任务,先执行新接收到的任务.
DiscardPolicy拒绝新任务:当工作线程已经忙不过来时,其他线程继续给工作线程分配任务,工作线程会继续手头上的任务,不理会这个新任务.
2.如何确定线程池的线程数目
线程池的线程数目是不能直接确定的,只能通过实验来测试多少线程合适.
原因在于:
1.cpu的配置不确定
2.程序执行的特点不确定:
程序执行可分为:
cpu密集型任务型(进行大量的算数运算和逻辑判断),占用cpu;
io密集型任务(进行大量的读写网卡/读写硬盘操作),不占用cpu;
有些程序进行很多cpu密集型任务,有些程序进行很多io密集任务,很难去量化这两种任务的比例.
如果100%是cpu密集型任务,cpu的核心数是N的话,线程数最大也就是N,在多了也没有意义,因为CPU已经被占满了,但是如果cpu密集型任务10%,io密集型任务90%,那整个程序几乎是在操作io读写,线程数是10N也是没关系,因为此时CPU有空闲.
因此在实际开发的过程中,最优的处理方案就是针对你的程序进行性能验证,分别给程序设置不同线程数目,比如N,1.5N,2.0N,0.5N,分别记录下每种情况下的一些核心性能指标和系统负载情况,然后找出一个你觉得最合适的数目配置.
🌏🌏🌏今天的你看懂这里又学到了很多东西吧🌏🌏🌏
🌔 🌔 🌔下次见喽🌔 🌔 🌔