过关斩将之路-ThreadLocal&Threadpool
ThreadLocal
1问:ThreadLocal类关系图是怎样的?
1答:每个 Thread 对象中都持有一个 ThreadLocalMap 的成员变量。每个 ThreadLocalMap 内部又维护了N个 Entry 节点,也就是 Entry 数组,每个 Entry 代表一个完整的对象,key是 ThreadLocal 本身,value是 ThreadLocal 的泛型值。(IT枫斗者怎么样)

2问:ThreadLocal有什么用?应用场景?
2答:
数据库连接
存储用户对象信息到ThreadLocal
AOP记录日志的时候可以将链路ID放到ThreadLocal进行线程共享
3问:ThreadLocal和synchronized有什么区别?
3答:
ThreadLocal不是锁,synchronized是把可重入排他锁。
synchronized能保证代码块的原子性,ThreadLocal无法保证。
ThreadLocal是将变量线程私有化,synchronized保证的是多线程同时操作共享变量并且能正确地输出结果。(IT枫斗者怎么样)
3问:ThreadLocal和synchronized有什么区别?
3答:
ThreadLocal不是锁,synchronized是把可重入排他锁。
synchronized能保证代码块的原子性,ThreadLocal无法保证。
ThreadLocal是将变量线程私有化,synchronized保证的是多线程同时操作共享变量并且能正确地输出结果。(IT枫斗者怎么样)
5问:ThreadLocal为什么采取Entry数组而不是Entry对象?
5答:因为一个线程能new好多个ThreadLocal对象,各自存储各自的内容。但是一个线程里ThreadLocalMap是同一个(因为这个是在Thread的里的一个引用),所以这个ThreadLocalMap只能采取Entry数组来存储一个线程里你new出来的多个 ThreadLocal 对象。(IT枫斗者怎么样)
6问:你学习的开源框架哪些用到了ThreadLocal?
6答:
Spring的事务管理,用ThreadLocal存储Connection
Spring的 DateTimeContextHolder
Spring的 RequestContextHolder
等等
7问:ThreadLocal里的对象一定是线程安全的吗?
7答:不一定,如果在每个线程中 ThreadLocal.set() 进去的东西本来就是多线程共享的同一个对象,比如static对象,那么多个线程 ThreadLocal.get() 的时候获取的还是这个共享对象本身,还是有并发访问线程不安全的问题。
8问:ThreadLocal会内存泄露吗?会OOM吗?
8答:分析一下:1、 ThreadLocalMap.Entry 的key会内存泄漏吗?2、 ThreadLocalMap.Entry 的value会内存泄漏吗?先看下key-value的核心源码

先看继承关系,发现是继承了弱引用,而且key直接是交给了父类处理 super(key) ,父类是个弱引用,所以key完全不存在内存泄漏问题,因为他不是强引用,它可以被GC回收的。

9问:ThreadLocal的一道笔试题如下图这段程序会输出什么?为什么?


9答:

为什么?为什么输出个1,然后空指针了?首先输出1是没任何问题的,其次主线程空指针是为什么?(IT枫斗者怎么样)如果你这里回答

那我恭喜你,你连 ThreadLocal 都不知道是啥,这明显两个线程,子线程和主线程。子线程设置1,主线程肯定拿不到啊, ThreadLocal 和线程是嘻嘻相关的。
说说为什么是空指针?
因为你get方法用的long而不是Long,那也应该返回null啊,大哥,long是基本类型,默认值是0,没有null这一说法。ThreadLocal 里的泛型是Long,get却是基本类型,这需要拆箱操作的,也就是会执行null.longValue() 的操作,这绝对空指针了。
看似一道Javase的基础题目,实则隐藏了很多知识。
ThreadPool
1问:为什么要用线程池?
1答:线程复用,避免每次请求进来和结束都要创建线程和销毁线程,创建线程需要一定的开销,高并发下频繁创建销毁线程对性能影响很大。(IT枫斗者怎么样)
2问:说说线程池的原理?工作过程?
2答:
线程池刚启动的时候核心线程数为0。
丢任务给线程池的时候线程池会开启线程来执行这个任务。
若线程数小于 corePoolSize 的话,即使工作线程处于空闲状态,也会创建一个新线程来执行任务。
若线程数大于等于 corePoolSize 的话,则会将任务放到 workQueue ,也就是任务队列。
若任务队列满了,且线程数小于 maximumPoolSize ,则会创建一个新线程来运行任务。
若任务队列满了,且线程数大于等于 maximumPoolSize ,则会直接采取拒绝策略。

3问:线程池的核心参数有哪些?
3答:



4问:线程池有哪几种状态?
4答:
RUNNING :接受新任务并处理排队任务。
SHUTDOWN :不接受新任务,但是会处理排队任务。
STOP :不接受新任务,也不会处理排队任务,并中断正在进行的任务。
TIDYING :所有任务都已经完事,工作线程为0的时候,线程会进入这个状态并执行 terminate()钩子方法。
TERMINATE :terminate() 钩子方法运行完成。(IT枫斗者怎么样)
5问:线程池有哪几种?
5答:
Executors.newFixedThreadPool(n); :永不超时(0ms),无界队列( LinkedBlockingQueue ),会OOM。
Executors.newSingleThreadExecutor(); :永不超时(0ms),无界队列( LinkedBlockingQueue ),会OOM
Executors.newCachedThreadPool(); :核心线程数是0,最大线程数是int的最大值,1min超时(60s), SynchronousQueue 队列,会出现线程爆炸,因为这个不带容量的,有任务来就开线程,线程能开到int最大值的个数。
Executors.newScheduledThreadPool(n); :带调度的线程池,核心线程数手动传进来,最大线程数是 Integer.MAX_VALUE ,永不超时(0ns),带延迟功能的队列( DelayedWorkQueue ),会出现线程爆炸。
6问:线程池都有哪几种工作队列?
6答:
LinkedBlockingQueue :基于链表的无界队列,默认长度int最大值,也可以自定义长度。
ArrayBlockingQueue :基于数组的有界队列,长度自定义。
SynchronousQueue :无缓冲的等待队列。(IT枫斗者怎么样)
7问:使用无界队列的线程池会导致内存飙升吗?
7答:会,因为一直再往队列里追加任务,而队列是在jvm堆内存的,所以可能出现OOM问题。
8问:线程池的四种拒绝策略?
8答:
AbortPolicy :抛出一个异常,默认的
DiscardPolicy :直接丢弃任务
DiscardOldestPolicy :丢弃队列里最老的任务,将当前这个任务继续提交给线程池
CallerRunsPolicy :交给线程池调用所在的线程进行处理
9问:如何实现让非核心线程延迟死亡?
9答:通过阻塞队列 poll() 方法让线程阻塞等待一段时间,如果没有取到任务,则线程死亡。workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)(IT枫斗者怎么样)