-
Entry 的 Key 是一个 ThreadLocal 实例,Value 是一个线程特有对象。Entry 的作用即是:为其属主线程建立起一个 ThreadLocal 实例与一个线程特有对象之间的对应关系;
-
Entry 对 Key 的引用是弱引用;Entry 对 Value 的引用是强引用。
6)线程池有了解吗?(必考)
答:java.util.concurrent.ThreadPoolExecutor 类就是一个线程池。客户端调用 ThreadPoolExecutor.submit(Runnable task) 提交任务,线程池内部维护的工作者线程的数量就是该线程池的线程池大小,有 3 种形态:
- 当前线程池大小 :表示线程池中实际工作者线程的数量;
- 最大线程池大小 (maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限;
- 核心线程大小 (corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限。
-
如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;
-
如果运行的线程等于或者多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不是添加新线程;
-
如果无法将请求加入队列,即队列已经满了,则创建新的线程,除非创建此线程超出 maxinumPoolSize, 在这种情况下,任务将被拒绝。
面试官:我们为什么要使用线程池?
答:
-
减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
-
可以根据系统的承受能力,调整线程池中工作线程的数目,放置因为消耗过多的内存,而把服务器累趴下(每个线程大约需要 1 MB 内存,线程开的越多,消耗的内存也就越大,最后死机)
面试官:核心线程池内部实现了解吗?
答:对于核心的几个线程池,无论是 newFixedThreadPool() 方法,newSingleThreadExecutor() 还是 newCachedThreadPool() 方法,虽然看起来创建的线程有着完全不同的功能特点,但其实内部实现均使用了 ThreadPoolExecutor 实现,其实都只是 ThreadPoolExecutor 类的封装。
为何 ThreadPoolExecutor 有如此强大的功能呢?我们可以来看一下 ThreadPoolExecutor 最重要的构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
函数的参数含义如下:
-
corePoolSize:指定了线程池中的线程数量
-
maximumPoolSize:指定了线程池中的最大线程数量
-
keepAliveTime:当线程池线程数量超过 corePoolSize 时,多余的空闲线程的存活时间。即,超过了 corePoolSize 的空闲线程,在多长时间内,会被销毁。
-
unit: keepAliveTime 的单位。
-
workQueue:任务队列,被提交但尚未被执行的任务。
-
threadFactory:线程工厂,用于创建线程,一般用默认的即可。
-
handler:拒绝策略。当任务太多来不及处理,如何拒绝任务。
7)Atomic关键字:
答:可以使基本数据类型以原子的方式实现自增自减等操作。参考博客:concurrent.atomic包下的类AtomicInteger的使用
8)创建线程有哪几种方式?
答:有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。
面试官:两种方式有什么区别呢?
- 继承方式:
-
(1)Java中类是单继承的,如果继承了Thread了,该类就不能再有其他的直接父类了.
-
(2)从操作上分析,继承方式更简单,获取线程名字也简单.(操作上,更简单)
-
(3)从多线程共享同一个资源上分析,继承方式不能做到