欢迎光临散文网 会员登陆 & 注册

过关斩将之路-ThreadLocal&Threadpool

2023-04-22 10:57 作者:IT枫斗者-跳蚤网  | 我要投稿

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枫斗者怎么样


过关斩将之路-ThreadLocal&Threadpool的评论 (共 条)

分享到微博请遵守国家法律