image-20201126113618828
银行实际上就是一个Java线程池。
一次周末去银行办业务,人多排号,排的久了突然发现银行实际上和Java的线程池如此类似,可以说一模一样。于是我就展开了联想。
以上银行的流程和线程池的流程完全是一样的,我们只需要将
上述的步骤就和线程池的原理,基本参数一模一样了。
说完了线程池的基本原理,接下来就是线程池常见的面试问题。
谈到线程池就会想到池化技术,其中最核心的思想就是把宝贵的资源放到一个池子中;每次使用都从里面获取,用完之后又放回池子供其他人使用,有点吃大锅饭的意思。
Java线程池有以下优点:
在Java中,我们可以通过Executors类创建线程池,常见的API有:
以上的这些创建线程池的方法,实际上JDK已经给我们写好的,可以拿来即用的。但是只要我们查看上述方法的源码就会发现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
以上方法实际上都是利用 ThreadPoolExecutor 类实现的。
所以第二种创建线程方式是自己通过 new ThreadPoolExecutor来进行创建。
答案:一个都不用。
从《阿里巴巴Java开发手册》中可以看到
image-20200728104421665
在上一个问题中,我们提到了创建线程池要通过 new ThreadPoolExecutor 的方式,那么,如何创建呢?在创建的时候,又需要哪些参数呢?
我们直接看一下 ThreadPoolExecutor 的构造方法源码,如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
密密麻麻都是参数,那么这些参数都什么呢?
image-20200728104546690
大致的流程就是
通过以上参数可以就可以灵活的设置一个线程池了,示例代码如下:
/**
* 获取cpu核心数
*/
private static int corePoolSize = Runtime.getRuntime().availableProcessors();
/**
* corePoolSize用于指定核心线程数量
* maximumPoolSize指定最大线程数
* keepAliveTime和TimeUnit指定线程空闲后的最大存活时间
*/
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1000));
关于线程池的工作原理和执行流程,通过两张图来进行展示
image-20201126094135666
image-20201126094144097
所谓饱和策略就是:当等待队列已经排满,再也发不下新的任务的时候,这时,线程池的最大线程数也到了最大值,意味着线程池没有能力继续执行新任务了,这个时候再有新任务提交到线程池,如何进行处理,就是饱和(拒绝)策略
image-20201126094213570
通常我们是需要根据这批任务执行的性质来确定的。
当然这些都是经验值,最好的方式还是根据实际情况测试得出最佳配置。
关闭线程池的方法有两个:shutdown()/shutdownNow()
。
shutdown()
执行后停止接受新任务,会把队列的任务执行完毕。shutdownNow()
也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。关闭线程池的代码:
long start = System.currentTimeMillis();
for (int i = 0; i <= 5; i++) {
pool.execute(new Job());
}
pool.shutdown();
while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
LOGGER.info("线程还在执行。。。");
}
long end = System.currentTimeMillis();
LOGGER.info("一共处理了【{}】", (end - start));
pool.awaitTermination(1, TimeUnit.SECONDS)
会每隔一秒钟检查一次是否执行完毕(状态为 TERMINATED
),当从 while 循环退出时就表明线程池已经完全终止了。
如果你觉得这篇内容对你挺有有帮助的话: