最后
无论是哪家公司,都很重视基础,大厂更加重视技术的深度和广度,面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。
针对以上面试技术点,我在这里也做一些分享,希望能更好的帮助到大家。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue 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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
参数说明:
-
corePoolSize:核心池的线程数量;
-
workQueue:用于保存任务的工作队列;
-
maximumPoolSize:最大线程池的大小;
-
keepAliveTime:当线程数量大于核心池线程数量时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间,多余的线程会被终止;
-
TimeUnit:keepAliveTime的单位;
-
ThreadFactory:线程工厂,可以给线程设置名字;
-
handler:饱和策略。当队列和线程池都满了,会触发饱和策略,来处理新提交的任务。饱和策略以下几种:AbortPolicy:直接抛出异常;CallerRunsPolicy:只用调用者所在线程来运行任务;DiscardOldestPolicy:丢弃最近一个任务并执行当前任务;DiscardPolicy:不处理,丢弃掉。
使用Executors创建线程池
====================
使用工具类Executors可创建三种类型的线程池:FixedThreadPool、SingleThreadExecutor、CachedThreadPool。本质上也是调用上述构造方法。理解了前文的参数解释,下面三种线程池也就容易理解了。
- FixedThreadPool
可重用固定线程数的线程池。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
工作流程如下:
-
如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务;
-
线程数等于corePoolSize之后,新任务加入LinkedBlockingQueue(无界阻塞队列)。因为最大线程数maximumPoolSize参数值等于corePoolSize,不会产生多余线程;
-
线程执行完任务之后会反复从LinkedBlockingQueue中获取任务来执行。
- SingleThreadExecutor
单个worker线程的线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
SingleThreadExecutor与FixedThreadPool的区别在于,maximumPoolSize和corePoolSize都设置成了1,其它参数都一样。
- CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
CachedThreadPool将corePoolSize设置为0,maximumPoolSize设置为无限大,同时使用了一个没有容量的工作队列SynchronousQueue。这个线程池没有固定的核心线程,而是根据需要创建新线程。
工作流程:
-
有新任务时,主线程执行SynchronousQueue.offer操作,空闲线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)操作,配对成功则将任务交给空闲线程执行;
-
当没有空闲线程时,上面的配对操作失败,此时会创建一个新线程来执行任务;
-
任务执行完毕后,空闲线程会等待60秒。60秒内如果有新任务,就立即执行,否则时间一过线程就终止。
线程池关闭
=========
调用shutdown或者shutdownNow方法可关闭线程池。原理是遍历线程池中所有工作线程,调用interrupt方法来中断线程。
-
shutdown:将线程置为SHUTDOWN状态,不能接受新的任务,等待所有任务执行完毕;
-
shutdownNow:将线程置为STOP状态,不能接受新的任务,尝试去终止正在执行的恶任务;
这里涉及到ThreadPoolExecutor中定义的线程的五种状态
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
-
RUNNING:接受新任务,处理任务;
-
SHUTDOWN:不接受新任务,但会把队列中任务处理完;
-
STOP:不接受新任务,不处理队列中的任务,并且终止正在处理的任务;
-
TIDYING:正在执行的任务和队列都为空,进入该状态,将要执行terminated();
-
TERMINATED:所有terminated()方法执行完毕,线程池彻底终止。
当队列和正在执行的任务都为空时,由SHUTDOWN转化为TIDYING;当正在执行的任务为空,由STOP转化为TIDYING。
本博客从线程池的原理介绍作为切入点,分析了线程池中尤为关键的组件:阻塞队列。同时分析了线程池的核心实现类ThreadPoolExecutor。以线程池的创建和关闭的思路,梳理了相关知识点,包括三种常用线程池介绍以及线程池五种状态。
Java核心架构进阶知识点
面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Java核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、Spring相关、分布式、微服务、RPC、网络、设计模式、MQ、Redis、MySQL、设计模式、负载均衡、算法、数据结构、kafka、ZK、集群等。而这些也全被整理浓缩到了一份pdf——《Java核心架构进阶知识点整理》,全部都是精华中的精华,本着共赢的心态,好东西自然也是要分享的
内容颇多,篇幅却有限,这就不在过多的介绍了,大家可根据以上截图自行脑补
308164)]
内容颇多,篇幅却有限,这就不在过多的介绍了,大家可根据以上截图自行脑补