写点什么

Java- 技术专题 - 挖掘陷阱系列(1-10)

发布于: 2021 年 03 月 16 日
Java-技术专题-挖掘陷阱系列(1-10)

1.开发过程中使用的 ThreadLocal 出现的问题

  • 可能出现数据紊乱:使用线程池之后,没有强制清楚每一个线程内部的 ThreadLocal 对象的 ThreadLocalMap 的数据,因为线程池本身为复用线程的存在,一旦没有在线程的 init 方法中重置或者使用完清空,则会出现数据紊乱,获取到上一个线程执行的数据。

  • 可能出现内存泄漏:因为考虑到 ThreadLocalMap 存在内部的 Entry 对象,为 weakReference 对象弱引用,非强引用对象(此处误解,不是内存泄漏的根本原因),主要是 ThreadLocalMap 对象中的对象引用的生命周期与 Thread 的线程所在生命周期持平,因为 key 为弱引用,故此会被清除 null,此后,只有在 ThreadLocalMap 获取 get 方法或者 set 的时候,value 才会被回收,故此只有在手动清除之后,才是最完美的解决方案。

2.开发过程中使用的 ConcurrentHashMap 一定所有操作都是线程安全的吗

  • ConcurrentHashMap 只能保证的原子读写操作(putIfAbsent、computeIfAbsent、putIfPresent、replace、compute)等操作属于原子且线程安全的。

  • 如果要确保多个原子行操作整体的线程安全,需要自己手动加锁,(size、isEmpty、containsValue)等聚合方法,并发时候可能反映的就是 ConcurrentHashMap 的中间状态。因此在并发情况下,返回值只能作为参考使用。

3.开发过程中使用的 ConcurrentHashMap 但是性能没有的到提高

  • ConcurrentHashMap 使用了那样如 HashMap 的方式进行加锁的方式操作,使用 ConcurrentHashMap

  • 建议可以考虑使用原子化操作的 CAS 服务的轻量级乐观锁进行控制处理操作机制。(computeIfPresent、computeIfAbsent、putIfAbsent,getOrDefault())

4.开发过程中使用了 CopyOnWriteArrayList 导致的性能问题

  • 因为目前的 COW 服务框架,使用的为两个内部拷贝的数据集合进行切换读写快照,使得读操作和写操作进行相互隔离机制,同时也避免了大量的拷贝数据的开销。

  • 但伴随的相同的问题,仍有 50%的数据的空间浪费,所以如果充分考虑的数据集合最好为一次性初始化 COW 容器或者很少更新或者操作 COW 中数据信息的场景我们才会使用 COW 容器技术。

5.开发过程中没有理清楚线程安全的问题所在点,导致锁无效

  1. 需要识别线程不安全的范围和原因以及场景问题,在对症下药,进行控制相关的锁的范围以及粒度。

  2. 单纯考虑 Lock 锁的概念基本属于实例或者方法范围。资源更多的与场景和类有关系,所以要多注意是否资源和锁的关系和范围

6.开发过程中,没有考虑锁的粒度,可能会导致性能的问题。

  1. 单纯考虑锁的范围,可能会因小失大,所以锁的粒度如果过大,会导致整体代码无法优化以及串行化处理无法实现多核 cpu 的运作机制,大大降低吞吐以及降低性能。

  2. 所以希望可以尽可能的降低锁的粒度,对必要的代码块或者资源进行管控保护即可

7.开发过程中,加锁没有考虑锁的场景,可能导致的性能的问题。

  • 使用 StampedLock 的乐观锁工具对象,可以实现大大提高一些服务性能。

  • 使用 ReadWriteLock 的读写锁可以将读和写场景划分的非常合理以及可管控。

  • 使用 Lock 的时候一定需要多关注一下是否考虑真正的开启公平锁的方式,公平锁会导致队列排序以及性能下降。

8.开发过程中,多把锁时,要格外的小心死锁的问题(VisualVM)

  • 在加锁的时候一定要注意锁的粒度和范围,尽可能的不要进行锁资源的交叉和重叠问题

  • 在加锁的时候一定要主要锁的顺序和关系,最重要是释放锁的顺序也要保持绝对一致。

9.开发过程中,使用 Executors 工具生成像城池导致两种类型的 OOM

  • 队列任务堆积过多的时候导致 OOM:newFixedThreadPool(队列为 LinkedBlockingQueue)和 newSingleThreadExecutor(队列为 LinkedBlockingQueue)

  • 创建线程过多会导致出现 OOM:newCachedThreadPool(队列为 SynchonousQueue)和 newScheduledThreadPool()都是属于最大值线程为 Integer.max 值 会导致几乎无限的创建线程最后导致 OOM 发生。

  • 故此建议考虑手动指定线程池的参数信息控制以及名字。

10.线程池的管理策略详解:如何实现一个更激进的线程池

  • 重写自定义一个新的 BlockingQueue 的类对象机制,主要针对于相关的 BlockingQueue 的 offer 方法,不进入队列,直接返回 false,这样就会进入相关的创建线程。

  • 重写自定义一个新的 RejectedExecutionHandler 的拒绝策略时候在把任务加入任务队列。


用户头像

我们始于迷惘,终于更高的迷惘。 2020.03.25 加入

一个酷爱计算机技术、健身运动、悬疑推理的极客狂人,大力推荐安利Java官方文档:https://docs.oracle.com/javase/specs/index.html

评论

发布
暂无评论
Java-技术专题-挖掘陷阱系列(1-10)