看完这篇文档,来检验下你的掌握情况,看看你是否可以找出其中的问题:https://www.nowcoder.com/discuss/859437338936487936
线程池主要由两个概念组成:一个是任务队列;另一个是工作者线程。工作者线程主体就是一个循环,循环从队列中接受任务并执行,任务队列保存待执行的任务。ThreadPoolExecutor继承自AbstractExecutorService,实现了ExecutorService。ThreadPoolExecutor实现了生产者/消费者模式,工作者线程就是消费者,任务提交者就是生产者,线程池自己维护任务队列。当我们碰到类似生产者/消费者问题时,应该优先考虑直接使用线程池,而非“重新发明轮子”,应自己管理和维护消费者线程及任务队列。两个构造函数public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,        long keepAliveTime, TimeUnit unit,         BlockingQueue<Runnable> workQueue) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,        long keepAliveTime, TimeUnit unit,         BlockingQueue<Runnable> workQueue,        ThreadFactory threadFactory, RejectedExecutionHandler handler)第二个构造方法多了两个参数threadFactory和handler,这两个参数一般不需要,第一个构造方法会设置默认值。参数说明入参说明corePoolSize核心线程个数表示线程池中的核心线程个数,不是一开始就创建这么多线程,刚创建一个线程池后,实际上并不会创建任何线程。有新任务到来的时候,如果当前线程个数小于corePoolSiz,就会创建一个新线程来执行该任务,需要说明的是,即使其他线程现在也是空闲的,也会创建新线程。线程个数小于等于corePoolSize时,默认情况下:核心线程不会预先创建,只有当有任务时才会创建。核心线程不会因为空闲而被终止,keepAliveTime参数不适用于它。不过,ThreadPoolExecutor有如下方法,可以改变这个默认行为://预先创建所有的核心线程public int prestartAllCoreThreads()//创建一个核心线程,如果所有核心线程都已创建,则返回falsepublic boolean prestartCoreThread()//如果参数为true,则keepAliveTime参数也适用于核心线程public void allowCoreThreadTimeOut(boolean value)maximumPoolSize最大线程个数表示线程池中的最多线程数,线程的个数会动态变化,但这是最大值,不管有多少任务,都不会创建比这个值大的线程个数。如果线程个数大于等于corePoolSize,那就不会立即创建新线程了,它会先尝试排队,需要强调的是,它是“尝试”排队,而不是“阻塞等待”入队,如果队列满了或其他原因不能立即入队,它就不会排队,而是检查线程个数是否达到了maximumPoolSize,如果没有,就会继续创建线程,直到线程数达到maximumPoolSize。keepAliveTimeunit空闲线程存活时间keepAliveTime的目的是为了释放多余的线程资源,它表示,当线程池中的线程个数大于corePoolSize时额外空闲线程的存活时间。也就是说,一个非核心线程,在空闲等待新任务时,会有一个最长等待时间,即keepAliveTime,如果到了时间还是没有新任务,就会被终止。如果该值为0,则表示所有线程都不会超时终止。workQueue任务队列要求的队列类型是阻塞队列BlockingQueue。注意:如果用的是无界队列LinkedBlockingQueue,线程个数最多只能达到corePoolSize,到达corePoolSize后,新的任务总会排队,参数maximumPoolSize也就没有意义了。对于没有实际存储空间的同步阻塞队列SynchronousQueue,当尝试排队时,只有正好有空闲线程在等待接受任务时,才会入队成功,否则,总是会创建新线程,直到达到maximumPoolSize。threadFactory线程工厂类型为接口ThreadFactory:public interface ThreadFactory {    Thread newThread(Runnable r);}这个接口根据Runnable创建一个Thread, ThreadPoolExecutor的默认实现是Executors类中的静态内部类DefaultThreadFactory,主要就是创建一个线程,给线程设置一个名称,设置daemon属性为false,设置线程优先级为标准默认优先级,线程名称的格式为:pool-<线程池编号>-thread-<线程编号>。如果需要自定义一些线程的属性,比如名称,可以实现自定义的ThreadFactory。handler任务拒绝策略如果队列有界,且maximumPoolSize有限,则当队列排满,线程个数也达到了maximumPoolSize,这时,新任务来了,会触发线程池的任务拒绝策略。ThreadPoolExecutor实现了4种处理方式:ThreadPoolExecutor.AbortPolicy:这就是默认的方式,抛出异常。ThreadPoolExecutor.DiscardPolicy:静默处理,忽略新任务,不抛出异常,也不执行。ThreadPoolExecutor.DiscardOldestPolicy:将等待时间最长的任务扔掉,然后自己排队。ThreadPoolExecutor.CallerRunsPolicy:在任务提交者线程中执行任务,而不是交给线程池中的线程执行。这些拒绝策略都实现RejectedExecutionHandler接口:public interface RejectedExecutionHandler {    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}当线程池不能接受任务时,调用其拒绝策略的rejectedExecution方法。注意:拒绝策略只有在队列有界,且maximumPoolSize有限的情况下才会触发。如果队列无界,服务不了的任务总是会排队,但这不一定是期望的结果,因为请求处理队列可能会消耗非常大的内存,甚至引发内存不够的异常。如果队列有界但maximumPoolSize无限,可能会创建过多的线程,占满CPU和内存,使得任何任务都难以完成。所以,在任务量非常大的场景中,让拒绝策略有机会执行是保证系统稳定运行很重要的方面。查看关于线程和任务数的一些动态数字:获取状态函数说明public int getPoolSize()返回当前线程个数public int getLargestPoolSize()返回线程池曾经达到过的最大线程个数public long getCompletedTaskCount()返回线程池自创建以来所有已完成的任务数public long getTaskCount()返回所有任务数,包括所有已完成的加上所有排队待执行的工厂类Executors类Executors提供了一些静态工厂方法,可以方便地创建一些预配置的线程池,主要方法有:public static ExecutorService newSingleThreadExecutor() public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newCachedThreadPool()方法说明使用场景newSingleThreadExecutor只使用一个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止,该线程顺序执行所有任务。需要确保所有任务被顺序执行的场合。newFixedThreadPool使用固定数目的n个线程,使用无界队列LinkedBlockingQueue,线程创建后不会超时终止。和newSingleThreadExecutor一样,由于是无界队列,如果排队任务过多,可能会消耗过多的内存。在系统负载很高的情况下,newFixedThreadPool可以通过队列对新任务排队,保证有足够的资源处理实际的任务。newCachedThreadPool它的corePoolSize为0, maximumPoolSize为Integer.MAⅩ_VALUE, keepAliveTime是60秒,队列为SynchronousQueue。它的含义是:当新任务到来时,如果正好有空闲线程在等待任务,则其中一个空闲线程接受该任务,否则就总是创建一个新线程,创建的总线程个数不受限制,对任一空闲线程,如果60秒内没有新任务,就终止。如果系统负载不太高,单个任务的执行时间也比较短,newCachedThreadPool的效率可能更高,因为任务可以不经排队,直接交给某一个空闲线程。注意:在系统负载可能极高的情况下,上述方式都不是好的选择,newFixedThreadPool的问题是队列过长,而newCachedThreadPool的问题是线程过多,这时,应根据具体情况自定义ThreadPoolExecutor,传递合适的参数。
点赞 0
评论 1
全部评论

相关推荐

牛客28967172...:跟着卡子哥才是正道,灵茶属实不太行
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务