Java多线程专题3: Thread和ThreadLocal
- 合集目录
- Java多线程专题3: Thread和ThreadLocal
进程, 线程, 协程的区别
进程 Process
进程提供了执行一个程序所需要的所有资源, 一个进程的资源包括虚拟的地址空间, 可执行的代码区, 可用的系统接口, 本地存储, 系统保障的安全上下文(security context), 唯一的进程ID, 环境变量, 优先级, 以及至少一个工作线程. 每个进程都是从一个主线程启动, 然后在运行中创建更多的线程.
- 进程是操作系统资源分配的最小单位
- 同一时刻在系统中执行的进程数不会超过核心数
- 进程之间是隔离的
- 进程之间有层级关系, 由内核直接创建的进程没有父进程, 除此以外进程都是由其它进程创建的
- 父进程退出并不会终止子进程
线程 Thread
线程是运行于进程中的一个可以调度运行的实例, 在一个进程中的所有线程, 共用这个进程的虚拟地址空间和其它系统资源(前面进程中提到的进程资源), 除此以外, 还有自己唯一的线程ID, 线程的上下文变量(用于系统调度), 这些上下文变量包含线程的寄存器, 内核堆栈, 环境变量等. 线程也有自己的安全上下文.
进程和线程的区别
- 进程是操作系统资源分配的基本单位,线程是独立运行和独立调度的基本单位
- 进程使用单独的地址空间, 而线程(同一个进程下)使用共享地址空间
- 线程创建和调度的开销比进程小
- 线程因为共享地址空间, 同一进程的线程间通信很方便
协程 Coroutine
协程是一个特殊的函数, 这个函数可以在运行中被"挂起:, 可以重新在挂起处继续运行. 协程是比线程更加轻量级的一种并发模式.
一个线程中的多个协程的运行是串行的, 一个线程内可以运行多个函数, 但这些函数都是串行运行的, 当一个协程运行时, 其他协程就处于挂起状态. 单独使用协程无法利用CPU的多核资源.
协程和线程的区别
线程是由操作系统调度的, 是操作系统层级的并发, 而协程是完全由程序所控制的, 即在用户态执行, 一个线程可以同时运行多个协程, 运行的开销更小. 线程可以利用CPU的多核资源, 协程不可以, 需要与线程或进程配合.
讲讲ThreadLocal 是什么
ThreadLocal 提供线程的局部变量, 每一个访问该变量的线程在 ThreadLocal 内部都有一个独立的初始化的变量, ThreadLocal 实例变量通常采用private static 在类中修饰. 每个线程都会持有自己的ThreadLocal 实例, 当一个线程结束时,它所持有的所有 ThreadLocal 实例会被回收. ThreadLocal 解决了多线程之间数据隔离问题.
ThreadLocal 使用场景
ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也就是变量在线程间隔离,而在同一线程共享的场景。例如管理Connection,我们希望每个线程只使用一个Connection实例,这个时候用ThreadLocal就很合适.
使用方法为
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.set(1);
threadLocal.get();
threadLocal.remove();
示例代码
public class Demo {
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
threadLocal.set(new Object());
someMethod();
}
static void someMethod() {
// 获取在threadLocal中存储的对象
threadLocal.get();
// 处理
dosomthing();
// 清除
threadLocal.remove();
}
}
ThreadLocal 作为变量的线程隔离是如何实现的?
ThreadLocal 实现原理是什么?
在了解了如何使用之后,看下 ThreadLocal 是如何实现的
ThreadLocal.get()
看get方法的源码,可以看到方法中获取当前线程,并通过当前线程得到一个 ThreadLocalMap,然后通过 this(当前ThreadLocal对象)作为key,从Map中获取Entry
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
For example, the class generates unique identifiers local to each thread. A thread's id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.
通过 private static final 类型的变量实现. 在Thread实例中有一个 ThreadLocalMap类型的成员变量, 用来存多个ThreadLocal及其对应的值. 这时候可以new一个或多个ThreadLocal, 这个ThreadLocal实例会自动存入Thread的ThreadLocalMap里
private static final ThreadLocal threadSession = new ThreadLocal();
再往里面set值
threadSession.set(s);
做set操作时, 在ThreadLocalMap里以for循环的方式来取Entry, 判断key是否是这个ThreadLocal, 是的话才set.
用完后可以从ThreadLocalMap里面把这个ThreadLocal实例为key的值remove掉
threadSession.remove();
说说 InheritableThreadLocal 的实现原理?
InheritableThreadLocal 如何弥补 ThreadLocal 不支持继承的特性?
This class extends ThreadLocal to provide inheritance of values from parent thread to child thread: when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values. Normally the child's values will be identical to the parent's; however, the child's value can be made an arbitrary function of the parent's by overriding the childValue method in this class.
Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.
由于ThreadLocal设计之初就是为了绑定当前线程, 如果希望当前线程的ThreadLocal能够被子线程使用, 实现方式就会相当困难, 需要用户自己在代码中传递. InheritableThreadLocal主要用于子线程创建时, 需要自动继承父线程的ThreadLocal变量, 方便必要信息的进一步传递.
Thread类中包含 threadLocals 和 inheritableThreadLocals 两个变量, 其中 inheritableThreadLocals 即主要存储可自动向子线程中传递的ThreadLocal.ThreadLocalMap
采用默认方式产生子线程时, inheritThreadLocals=true;若此时父线程inheritableThreadLocals不为空, 则将父线程inheritableThreadLocals传递至子线程
ThreadLocalRandom 如何利用 ThreadLocal 的原理来解决 Random 的局限性?
Thread里有一个 threadLocalRandomSeed 变量, 就是专门用来给ThreadLocalRandom作为seed使用的, 里面还有一个threadLocalRandomProbe变量, 用户判断threadLocalRandomSeed是否已经初始化, 如果probe不为0, 则seed已经初始化.
SpringMVC 中如何使用 ThreadLocal 实现 request scope 作用域 Bean?
对应的代码, 可以看到 RequestAttributes 是用 ThreadLocal 类型存储的
public abstract class RequestContextHolder {
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
if(attributes == null) {
attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();
}
return attributes;
}