使用不熟悉的技术特性,更要知道里面的坑

55 阅读2分钟

例子一:不当使用InheritableThreadLocal

InheritableThreadLocal相比ThreadLocal多一个能力:在创建子线程Thread时,子线程Thread会自动继承父线程的InheritableThreadLocal信息到子线程中,进而实现在在子线程获取父线程的InheritableThreadLocal值的目的。

如果使用InheritableThreadLocal+线程池,那么在线程池的线程复用的时候,threadlocal值会被污染。因为只有在创建新线程的时候,才发生父子线程的threadlocal值的拷贝。

InheritableThreadLocal(其实ThreadLocal也一样)不适合应用于业务代码中,因为他们都是隐式的参数传递,而业务系统中好维护的代码应当是显式的参数传递。

以上内容摘抄自juejin.cn/post/744376… 这篇文章我认为写得很不错,很挺的理由是, 我也认可“框架类代码才是某些语言的高级用法主要发光发热的地方,因为对应的研发水平通常较高,且代码经过严格测试验证,并且较少变动。而业务系统研发水平参差不齐”,如果不是把原理吃得很透,其实很容易发生生产问题。

小插曲:其实我日常开发也差点用InheritableThreadLocal来解决异步交易的流水号丢失问题,最终考虑到上述文章提到的同样问题,没有使用,而是换成用Spring的装饰器模式(实现taskdecorator接口)来解决这个问题。

例子二.CompletableFuture

坑一:使用默认线程池

CompletableFuture默认的配置更适用于CPU密集型任务,但是实际业务开发中更多使用的是IO密集型任务。

如果你在使用CompletableFuture没有指定线程池,就会使用默认的ForkJoinPool。ForkJoinPool是基于"工作窃取"算法 (Work Stealing Algorithm),该算法的核心思想是每个工作线程有自己的任务队列(双端队列, Deque)。当一个线程完成了自己队列中的任务时,便会窃取其他线程队列中的任务执行,这样就不会因为某个线程在等待而浪费 CPU 资源。

CompletableFuture是否使用默认线程池的依据,和机器的CPU核心数有关。当CPU核心数-1大于1时,才会使用默认的线程池,否则将会为每个CompletableFuture的任务创建一个新线程去执行。

即便指定了自定义线程池,也必须做到线程池隔离。不同的业务不要用同一个线程池,要为自己的业务量身定制线程池,不然就会相互干扰。

坑二:跟openfeign混用导致报错

juejin.cn/post/712486… 在里面调用openfeign的话需要自定义线程池

例子三.响应式编程webflux带来的坑

juejin.cn/post/741920…