Java笔记之ThreadLocal
在JDK官方API文档中关于ThreadLocal的描述是这样的:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。 在实际的使用中,ThreadLocal既可以实现数据共享又可以实现数据隔离,举个例子,当我们做一个数据库转账操作的时候,如果手动控制事务,那么就可以使用ThreadLocal,将connection和ThreadLocal绑定,每次要用到connection对象的时候从当前线程获取,从而保证在不同的地方使用的connection对象是同一个,以此来控制事务。
关于线程间的数据隔离我们写以下代码测试:
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* @Author Mr.Gibson
* @Date 2020/6/8 10:29
* @Version 1.0.0
* @Description
*/
public class ThreadLocalTest implements Runnable{
private ThreadLocal<Student> studentThreadLocal =new ThreadLocal<>();
@Override
public void run() {
/**
*每个线程的ThreadLocal是同一个对象,那么他是如何实现同一个对象却存储了不同的student对象?
* 点开ThreadLocal的set方法:
* public void set(T value) {
* Thread t = Thread.currentThread();
* ThreadLocalMap map = getMap(t);
* if (map != null)
* map.set(this, value);
* else
* createMap(t, value);
* }
* 存在ThreadLocal里面的数据实际上是存在ThreadLocal里面的ThreadLocalMap里面的,而ThreadLocalMap的创建是根据当前线程创建的,
* 换句话说,每一个线程都有一个对应的ThreadLocalMap用来存储数据,由此可以实现数据隔离
* ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),
* 每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。
*/
System.out.println(studentThreadLocal);
String currentThreadName = Thread.currentThread().getName();
System.out.println("currentThread is :" + currentThreadName);
Random random = new Random();
int age = random.nextInt(100);
System.out.println(currentThreadName + " is setting age:" + age);
Student student = getStudent();
System.out.println(student);//每个线程的student对象是不一样的,由此可暗示数据必被隔离
student.setAge(age);
System.out.println(currentThreadName + " is first get age :" + student.getAge());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThreadName + " is second get age" + student.getAge());
}
private Student getStudent() {
Student student = studentThreadLocal.get();
if (student == null){
student = new Student();
studentThreadLocal.set(student);
}
return student;
}
public static void main(String[] args) {
ThreadLocalTest t1 = new ThreadLocalTest();
new Thread(t1,"ThreadA").start();
new Thread(t1,"ThreadA").start();
new Thread(t1,"ThreadC").start();
new Thread(t1,"ThreadD").start();
}
}
class Student{
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
从运行结果可以看出,线程中的ThreadLocal是同一个,当我们获取Student对象的时候都是通过get方法获取,然而却是不同的student对象,让我们点开
ThreadLocal的set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看出ThreadLocal中存储数据的是ThreadLocalMap,而这个map的创建是根据线程创建的,因此每个线程的map是不同的,因此可以实现数据隔离!