线程间数据传递之ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
1、ThreadLocal介绍
spring 中基于 ThreadLocal 来实现事务。
多线程 访问同一个共享变量的时候容易出现并发问题,ThreadLocal是除了加锁这种同步方式之外的一种保证
规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是
线程自己的变量这样就不会存在线程不安全问题。在实际多线程操作的时候,操作的是自己本地内存中的变量,从
而规避了线程安全问题。
ThreadLocal又叫做线程本地变量是为每一个Thread创建的一个变量的副本,每个线程都可以在内部访问到这个副
本。通过这种方式我们在一个线程的生命周期以内安全的访问这个变量,不用担心被其他线程所污染。这是它相对
于全局变量所带来的优势。
package com.example.threadlocalandother;
/**
* @author tom
*/
public class ThreadLocalDemo {
private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
private void fun1() {
threadLocal.set(System.nanoTime());
System.out.println("fun1:" + threadLocal.get());
fun2();
}
private void fun2() {
System.out.println("fun2:" + threadLocal.get());
fun3();
}
private void fun3() {
System.out.println("fun3:" + threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args) {
ThreadLocalDemo demo = new ThreadLocalDemo();
demo.fun1();
}
}
# 程序输出
fun1:1511680153700
fun2:1511680153700
fun3:1511680153700
我们在先创建了一个本地变量threadLocal 在fun1中set值,在其余的方法中我们并没有通过方法传递的方式显示
的将值传递给其他方法,仅仅是通过threadLocal变量get的方式就可以获取到我们所需要的变量,从而实现了变量
的跨方法的传递。可能你觉得这样写没什么好处,我不用threadLocal直接用个全局变量照样可以实现数据的传
递,我们可以改造一下fun1方法。
package com.example.threadlocalandother;
/**
* @author tom
*/
public class ThreadLocalDemo1 {
private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
private void fun1() throws InterruptedException {
threadLocal.set(System.nanoTime());
System.out.println("fun1:" + threadLocal.get());
final Thread t1 = new Thread(() -> {
System.out.println("t1:" + threadLocal.get());
}, "t1");
t1.start();
t1.join();
fun2();
}
private void fun2() {
System.out.println("fun2:" + threadLocal.get());
fun3();
}
private void fun3() {
System.out.println("fun3:" + threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalDemo1 threadLocalDemo1 = new ThreadLocalDemo1();
threadLocalDemo1.fun1();
}
}
# 程序输出
fun1:3554613378000
t1:null
fun2:3554613378000
fun3:3554613378000
按照常理来想那么我们t1线程内也应该能获取到数据,但是结果大相径庭。实际上 threadLocal 对象只是作为了一
个 key,而真正存储数据的是每个线程自身的 thread 内持有的一个 ThreadLocalMap 的对象,而我们的 t1 线程
自然就不能获取到数据。如果使用后不 remove 可能会有内存泄漏的风险!
package com.example.threadlocalandother;
/**
* @author tom
*/
public class ThreadLocalDemo2 {
private ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public void run() {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set(System.nanoTime());
Long aLong = threadLocal.get();
threadLocal.remove();
System.out.println(aLong);
}
}).start();
}
}
public static void main(String[] args) {
ThreadLocalDemo2 threadLocalDemo2 = new ThreadLocalDemo2();
threadLocalDemo2.run();
}
}
# 程序输出
3042907777600
3042907791300
3042908175100
3042908275900
3042908395900
3042908245300
3042908563900
3042908182800
3042908589200
3042908716700
package com.example.threadlocalandother;
/**
* @author tom
*/
public class ThreadLocalDemo3 {
/**
* 创建ThreadLocal变量
*/
private static final ThreadLocal<Integer> LOCAL_VARIABLE = new ThreadLocal<>();
static void print(String str) {
// 打印当前线程本地内存中localVariable变量的值
System.out.println(str + ": " + LOCAL_VARIABLE.get());
// 清除当前线程本地内存中的localVariable变量
LOCAL_VARIABLE.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// 设置线程1中本地变量的值
LOCAL_VARIABLE.set(100);
print("Thread 1");
System.out.println("After remove: " + LOCAL_VARIABLE.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// 设置线程2中本地变量的值
LOCAL_VARIABLE.set(200);
print("Thread 2");
System.out.println("After remove: " + LOCAL_VARIABLE.get());
}
});
t1.start();
t2.start();
}
}
# 程序输出
Thread 1: 100
Thread 2: 200
After remove: null
After remove: null
package com.example.threadlocalandother;
/**
* @author tom
*/
public class UserContext implements AutoCloseable {
private final ThreadLocal<String> ctx = new ThreadLocal<>(