为什么要使用线程池?
降低资源消耗
通过重复利用现有的线程,降低创建新线程带来的损耗提高响应速度
当提交了新的任务,不需要等待线程创建完毕才开始执行任务,而是可以立即执行提高线程的可管理性
利用线程池技术,可以有效的对线程进行管理。 在说线程池原理之前,我们先看下创建线程池的几个基本参数 > 还记得创建线程池,有哪些基本参数么?corePoolSize(线程池的基本大小)
当用提交一个任务到线程池中,会立即创建一个线程,只有当线程数大于corePoolSize的大小,才会观察是否有空闲的线程能够执行这个任务runnableTaskQueue(任务阻塞队列)
当没有可用线程去执行任务并且当前线程数已经达到了 corePoolSize数量,无法创建新的线程的时候,新提交的任务会被塞入阻塞队列,等待执行。阻塞队列有如下几种 - ArrayBlockingQueue(有界阻塞队列) - LinkedBlockingQueue(链表阻塞队列) - SynchronousQueue(同步队列,一次只能塞入优格) - PriorityBlockingQueue(具有优先级的有界阻塞队列)maximumPoolSize(线程池最大数量)
如果有界阻塞队列满了,就会直接创建新的线程执行任务,但是线程数并不是随意创建的,当线程数大于这个值的话,就会停止创建ThreadFactory(创建线程的工厂)
一般用给创建出来的线程设置名字用的RejectedExecutionHandler(饱和策略)
当无法创建新的线程时,就会执行饱和策略,在jdk1.5中,线程池中提供了一下4中饱和策略AbortPolicy(直接抛出异常)
CallRunsPolicy(使用调用者当前线程去执行任务)
DiscardOldestPolicy(丢弃队列里面最近的一个任务,并执行当前任务)
DiscardPolicy(直接丢弃,不处理)
说完这些基本参数后,该说说线程池的原理了,其实把上面的内容串起来,就是线程池的原理了线程池内部到底是怎么运作的呢,我们来看看他的实现原理
新提交一个任务到线程池中线程池判断现在是否有可用的线程执行任务,如果没有则判断当前工作的线程数是否大于核心线程线程数个数,如果小于核心线程数,则创建一个线程执行任务,否则进入下个流程判断当前阻塞队列是否已经满了,如果没满,则把任务放入阻塞队列,否则执行下个流程判断当前线程个数是否超过最大线程个数,如果没有超过,则创建新的线程执行任务,否则直接执行饱和策略总算把线程池原理说完了,最后我们在说下提交任务的时候,execute和submit的区别
任务提交的时候submit和execute的区别?
execute
使用execute提交任务,是拿不到线程中的返回值的,这个方法一般用来不需要获取任务返回值时候用的,方法传入的是一个Runnable实例submit
使用submit提交任务,会返回一个future对象,调用他的get方法,会阻塞当前线程,等任务执行结束后,会获取到返回值的。传入的是一个callable实例 当我们不需要用线程池的时候,就需要关闭他了,线程池提供了两个方法用来关闭线程池, shutdown和shutdownnow,关闭的原理都是依次遍历所有线程,调用interrupt方法来中断线程。那么他们有什么区别呢?关闭线程池 shutdown和shutdownnow的区别
shutdown
将线程池的状态设置成shutdown状态,然后中断没有正在执行任务的线程,正在执行任务的线程不会被终止shutdownnow
将线程池的状态设置成stop状态,尝试停止所有线程,包括正在执行任务的线程。如何合理的设置线程池的个数呢?
当我们的任务是IO密集型的(与磁盘和网络打交道时间比较久),建议可以创建 2*cpu个数 的线程,在处理IO的时候,让出cpu给其它线程干活 当我们的任务是CPU密集型的(计算占用大量时间),应配置尽可能小的线程数,建议cpu个数+1 的线程,减少线程间的切换。总结:本篇博文通过几个疑问,让大家对线程理解的更加深刻,后续会更新更多的有关并发编程相关的教程,感谢关注。大家可以关注我的公众号"乐哉开讲",领取更多资料。