4 问教你搞定 java 中的 ThreadLocal
摘要:ThreadLocal 是除了加锁同步方式之外的一种保证规避多线程访问出现线程不安全的方法。
多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal 是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。
Q: ThreadLocal 的常见使用场景?
A:每个线程中需要维护 1 个不同的副本,但这个副本可能是某一个时刻一起塞入每个线程的, 只不过之后该副本的变化 不再受其他线程的影响。
常见场景有连接器管理模块 connectorManager, 每个线程持有的 connect 变量是单独使用的,不会互相影响或者需要加锁。原因就是将其作为副本放入每个线程,当线程启动连接或者关闭时,不影响其他线程里的 getConnect 方法。
Q: ThreadLocal 和 Synchronized 关键字的区别?
A:
Synchronized 是用时间的消耗,来换取数据同步以及互不冲突
ThreadLocal 则是用空间的消耗,来换取数据之间互不冲突(不涉及同步)
Q:TheadLocal 在每个线程中是以什么形式存储的?原理是什么
A:这篇文章讲解 ThreadLocal 源码讲解的蛮好的:
Java 并发编程:深入剖析
看完后用我自己的话总结一下就是:
- 在某个线程中调用 某 threadlocal.set(value)时, 其实就是在该线程中新建了 1 个 threalocalMap, 然后把 threadLocal 作为键,value 作为值,放进本线程的 threalocalMap 中。
- 当在线程中调用 threadlocal.get()的时候,就是从线程的 threadLocalMap 中获取这个 threadLocal 对应的值
如果 get 不到,则可以通过自定义 initValue 方法生成一个 threadLocal 的默认值
见如下图所示:
Q: 下面这个代码会报什么错?(例子改编自上面链接的文章)
public class Test {
ThreadLocal<String> stringLocal = new ThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
final Test test = new Test();
System.out.println(test.getString());
Thread thread1 = new Thread(){
public void run() {
System.out.println(stringLocal.get());
};
};
thread1.start();
thread1.join();
stringLocal.set("thread0")
System.out.println(test.getString());
}
}
在 Thread1 中,会报空指针,因为调用 get 之前没有做过 set, 此时做 get 会报错。
一种方式改成这样:
Thread thread1 = new Thread(){
public void run() {
stringLocal.set("thread1")
System.out.println(stringLocal.get());
};
};
另一种是给 stringLocal 设置默认值,这种一般用于能直接根据线程推导出初始值的情况:
ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
protected String initialValue() {
return xxx;
};
};
正确 set 之后, 答案就会返回 thread0 和 thread1, 且后续怎么 set,两边都不会互相影响各自的 threadLocal,虽然看起来是都用的是同一个 Test 里的成员。
转载自:https://juejin.cn/post/6989791907882729479