ThreadLocal类用来提供线程内部的局部变量,这种变量在多线程环境下访问(通过get与set访问)时能保证各个线程的变量相对独立于其他线程的变量.ThreadLocal实例通常说都是private static类型的,用于关联线程和线程上下文
2.基本使用总结:
- 线程并发:在多线程并发的场景下
- 传递数据:我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
- 线程隔离:每个线程的变量都是独立的,不会相互影响
2.1 常用方法
2.2使用场景
- 1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
- 2、线程间数据隔离
- 3、进行事务操作,用于存储线程事务信息。
- 4、数据库连接,Session会话管理。
如下例:当变量content经过绑定之后,每次线程执行时获取的都是自己的专属变量,不会出现变量与线程不匹配的情况
public class MyDemo01 {
ThreadLocal t2 = new ThreadLocal<>();
private String content;
// 当前线程绑定变量content
public String getContent() {
// return content;
return t2.get();
}
public void setContent(String content) {
// this.content = content;
t2.set(content);
}
public static void main(String[] args) {
MyDemo01 demo = new MyDemo01();
for (int i=0;i<5;i++){
Thread t1 = new Thread(){
@Override
public void run() {
demo.setContent(Thread.currentThread().getName()+"的数据");
System.out.println(Thread.currentThread().getName()+"--->"+demo.getContent());
}
};
t1.start();
}
输出结果
Thread-0--->Thread-0的数据 Thread-2--->Thread-2的数据 Thread-1--->Thread-1的数据 Thread-4--->Thread-4的数据 Thread-3--->Thread-3的数据3.ThreadLocal与synchronized的区别
如下代码:使用synchronized来实现数据访问
public class MyDemo02 {
private String content;
// 当前线程绑定变量content
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void main(String[] args) {
MyDemo02 demo = new MyDemo02();
for (int i=0;i<5;i++){
Thread t1 = new Thread(){
@Override
public void run() {
synchronized (MyDemo02.class){
demo.setContent(Thread.currentThread().getName()+"的数据");
System.out.println(Thread.currentThread().getName()+"--->"+demo.getContent());
}
}
};
t1.start();
}
}
}
| synchronized | ThreadLocal | |
| 原理 | 同步机制采取以时间换空间的方式来进行数据访问,将同步代码块锁住,限制每次只能一个线程访问, | 采取的是以空间换时间的方式,为每一个线程都提供了一份变量的副本,从而实现同时访问而互不干扰 |
| 侧重点 | 多个线程之间的资源的同步 | 多个线程中让每个线程之间的数据相互隔离,程序具有更高的并发性 |
ThreadLocal在一些特定场景下的优势:
- 传递数据:保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题
- 线程隔离:各个线程之间的数据相互隔离却又具有并发性,避免同步方式带来的性能损失
每个Thread维护一个ThreadLocalMap,这个Map的Key是ThreadLocal实例本身,value才是真正要存储的值Object
具体过程如下:
- 每个Thread线程内部都有一个Map(ThreadLocalMap)
- Map里面存储ThreadLocal对象(Key)和线程的变量副本(value)
- Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值
- 对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰
JDK8设计的好处:
- 每个map存储的entry数量变少
- 当Thread销毁的时候,ThreadLocalMap也会随之销毁,减少了内存的使用
内存泄漏相关概念
- Memory overflow: 内存溢出,没有足够的内存提供申请者去使用
- Memory leak: 内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或者无法释放,造成的系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果.内存泄漏的堆积将导致内存溢出
引用相关概念
java中的引用有4种类型:强 软 弱 虚 当前只介绍强作用和弱引用
- 强作用:最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还"活着",垃圾回收器就不会回收这种对象
- 软引用:仅有软引用指向的对象,只有发生gc且内存不足,才会被回收;
- 弱引用:垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收他的内存
为什么key使用弱引用?
如果使用强引用,当ThreadLocal 对象的引用(强引用)被回收了,ThreadLocalMap本身依然还持有ThreadLocal的强引用,如果没有手动删除这个key ,则ThreadLocal不会被回收,所以只要当前线程不消亡,ThreadLocalMap引用的那些对象就不会被回收, 可以认为这导致Entry内存泄漏。
未完待续........



