1 submit的Runnable为什么通过Future获取任务的执行结果
submit的Runnable被封装成了FutureTask对象,并且返回。
Runnable执行的结果是在哪里交给FutureTask的?
FutureTask重写了Runnable的run()方法,在这个run()方法中,将任务的返回值放在了FutureTask的outcome成员中。
这样在主线程中就可以获取到该任务的执行结果了。
如果想要获取任务真正的执行结果得传入一个Callable类型的任务,而不能是Runnable类型的任务。如果传入Runnable类型的任务,就算给了一个返回值,也不是Runnable任务的返回值,而就是这个指定的返回值。
2 beforeExecute和afterExecute有什么用
beforeExecute是在任务执行之前执行的,afterExecute是在任务执行之后执行的,可以在里面添加日志、监控、统计信息。比如统计各个线程的执行时间等。
3 线程池的任务队列
线程池的任务队列就是一个BlockingQueue,可以是ArrayBlockingQueue,这个时候任务队列是固定大小的,如果任务过多的话,就会出现处理不了的情况。
4 线程池的线程数目
4.1 corePoolSize
向线程池添加新的任务的时候,如果线程数小于corePoolSize的时候,就会为该任务新创建一个线程。
如果allowCoreThreadTimeOut没有设置的话,该线程池至多有corePoolSize这么多的线程可以一直idle状态而不退出。
4.2 maximumPoolSize
4.3 keepAliveTime
如果设置了allowCoreThreadTimeOut的话,所有的线程在没有任务的时候最多的生存时间,如果超过了keepAliveTime的话,就会被销毁。
如果没有设置allowCoreThreadTimeOut的话,核心线程是不受该值影响的。超过了corePoolSize的线程会有一个keepAliveTime的生存时间,超过了该时间没有新的任务的话,该线程会被销毁。
5 works集合和workQueue
works集合是线程池中的线程集合;workQueue是被挂起的任务集合。
5.1 workQueue的大小
如果是ArrayBlockingQueue,在创建线程池的时候,需要指定这个值。
5.2 works集合的大小
corePoolSize和maximumPoolSize配置的。
6 workQueue中等待的任务是如何被works消费掉的
每个worker的run()方法本质上是调用了runWorker函数,在这个函数中有一个while循环,不断的去workQueue中获取新的任务执行。如果workQueue中没有新的任务了,有两个情况,如果配置为不timeout的话,核心线程会在阻塞队列上睡眠,等待新的任务的到来;如果配置了timeout的话,核心线程会退出,以后有新的任务的时候再创建。
当ThreadPoolExecutor调用execute加入新的任务的时候,如果works不够corePoolSize的话又回新建新的线程到线程池。
也就是说,ThreadPoolExecutor的execute是生成者,不断加入新的任务,而每个work即每个线程的runWorker是消费者,不断的从workQueue中获取新的任务。
7 corePoolSize、maximumPoolSize和workQueue的关系
新加一个任务的时候,如果线程池中的线程数小于corePoolSize的话,直接添加新的线程,如果大于等于corePoolSize,先试图将新的任务放入workQueue中,如果放不下的话,就新建一个线程,但是总的线程数不能超过maximumPoolSize。如果线程数已经超过了maximumPoolSize,那么reject处理这个任务,将这个任务交给RejectedExecutionHandler去处理。
比如ThreadPoolExecutor的DiscardOldestPolicy这个策略,它把workQueue中最老的任务丢掉,然后把这个最新的任务投入执行。
20 参考资料
20.1
20.2