Bootstrap

【并发编程】详解线程池和ThreadLocal

🗄️ThreadLocal

作用

使得每个线程都有自己专属的本地变量,避免了多个线程竞争同一个资源。

原理

每一个线程都有一个ThreadLocalMap,最终的变量是存储在当前线程的ThreadLocalMap中,而不是ThreadLocal中。
键为ThreadLocal实例,值为Object。(ThreadLocalMap是ThreadLocal的静态内部类)

内存泄漏问题

  1. ThreadLocal实例不再被强引用,故被垃圾回收器回收,key变为null
  2. 线程持续存活,导致ThreadLocalMap长期存在,但entry却无法被垃圾回收,造成内存泄漏。

避免内存泄漏:使用完ThreadLocal后,务必调用**remove()**方法,可以显示地移除对应的entry。

跨线程传递ThreadLocal的值

  • InheritableThreadLocal:使用InheritableThreadLocal时,会在创建子线程时,令子线程继承父线程中的ThreadLocal值。但不支持线程池场景下的ThreadLocal值传递。
  • TransmittableThreadLocal:阿里巴巴开源的工具类,支持线程池场景下的ThreadLocal值传递。

🔗线程池

定义

管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是放回线程池中,等待下一个任务获取。

优点

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

创建

  1. 通过ThreadPoolExecutor构造函数创建

    在这里插入图片描述

  2. 通过Executors框架的工具类创建

    Executors.newCachedThreadPool():没有上限的线程池

    Executors.newFixedThreadPool(int):有上限的线程池

常见的参数

  • 核心线程数
  • 最大线程数 = 核心线程数+临时线程
  • 空闲线程最大存活时间
  • 任务队列
  • 线程工厂
  • 拒绝策略

拒绝策略

在这里插入图片描述

大小决定

为什么要设置大小?

  1. 线程池数量设置太小,如果同一时间有大量请求需要处理,可能导致大量请求堆积在任务队列中进而OOM(内存溢出)或任务丢失。
  2. 线程池数量设置太大,大量线程同时争取CPU,导致频繁的上下文切换,减低执行效率。

公式:

  • CPU密集型任务(N+1):加一是因为当任务发生中断时,CPU处于空闲状态,可以充分利用CPU的空闲时间。
  • I/O密集型任务(2N):线程在处理I/O时,CPU处于空闲状态,这时可以将CPU交给其他线程。

最佳线程数 = N*(1+WT/ST)

N:CPU核心数
WT:线程等待时间
ST:线程计算时间

线程获取流程

获取核心线程,没有则放入等待队列中,等待队列满了的话,就创建临时线程。如果已经达到了最大线程数,就采用拒绝策略。

在这里插入图片描述

线程池预热

  • prestartCoreThread():启动一个线程,等待任务,如果已达到核心线程数,这个方法返回 false,否则返回 true;
  • prestartAllCoreThreads():启动所有的核心线程,并返回启动成功的核心线程数。

达到核心线程数,这个方法返回 false,否则返回 true;

  • prestartAllCoreThreads():启动所有的核心线程,并返回启动成功的核心线程数。
;