不恰当的使用InheritableThreadLocal造成OOM服务重启
1. 问题分析:
- 生产环境内存缓慢上涨最终OOM服务重启, 主线程启动了一个守护线程, 等主线程结束修改标志变量结束守护线程, 想法是好的,但是这个标志变量没有做到共享,导致守护线程一直运行,每启动一个主线程就会多一个子线程,子线程收不到主线程停止的标志会一直执行下去导致内存越来大。
误以为InheritableThreadLocal可以实现主子线程双向通信共享变量,现实打脸,子线程只是在init的时候把主线程中的map 复制了一份,从此主子线程变量没有任何关系。
2. Demo场景复现
先说结论,InheritableThreadLocal父线程中创建子线程创建的那个时刻子线程会复制一个副本, 此后副本和元数据分离互不干扰
final static InheritableThreadLocal<Boolean> sonThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) throws Exception {
sonThreadLocal.set(true);
System.out.println("start " + sonThreadLocal.get());
System.out.println(Thread.currentThread().getName() + sonThreadLocal.get());
Thread thread = new Thread(() -> {
int i = 0;
while (sonThreadLocal.get() && i++ < 10) {
System.out.println(Thread.currentThread().getName() + sonThreadLocal.get());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
Thread.sleep(2000);
sonThreadLocal.set(false);
System.out.println(Thread.currentThread().getName() + sonThreadLocal.get());
thread.join();
System.out.println("end " + sonThreadLocal.get());
}
运行结果:
start true
maintrue
Thread-0true
Thread-0true
mainfalse
Thread-0true
Thread-0true
Thread-0true
Thread-0true
Thread-0true
Thread-0true
Thread-0true
Thread-0true
end false
3.结论:
重新认识一下InheritableThreadLocal
1. 文心一言回答
- InheritableThreadLocal 的设计并
不支持子线程将值传递给父线程
。它的主要特性是允许父线程中的 InheritableThreadLocal 变量的值自动传递给其创建的子线程
。这是通过 InheritableThreadLocal 的内部机制,在 Thread 类中的 init 方法里复制父线程的 InheritableThreadLocals 到子线程中实现的。 - 在多线程编程中,线程之间的数据传递通常是通过共享数据结构或显式地在线程之间传递参数来完成的。然而,Java 的线程模型并不支持子线程向父线程传递数据,因为父线程和子线程是独立运行的,它们之间的执行顺序和生命周期可能是不确定的。
- 如果你需要在子线程中处理数据并将结果返回给父线程,你可以考虑使用其他机制,如使用 Future 和 Callable 接口创建的可返回结果的线程,或者使用线程安全的队列或共享数据结构来在线程之间传递数据。这些机制都允许你以更加可控和可预测的方式在线程之间传递数据。
2. chatGpt 3.5回答
在 Java 中,InheritableThreadLocal 类是 ThreadLocal 类的一个子类,它提供了一种允许子线程从父线程继承值
的机制。
当使用 InheritableThreadLocal 对象存储值时,如果在父线程中设置了某个值,那么子线程将会继承
该值。这意味着在子线程中可以访问到父线程设置的值,即使子线程是在父线程创建后启动的。
下面是一个简单的示例代码,演示了 InheritableThreadLocal 类的用法:
public class InheritableThreadLocalExample {
private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
// 在父线程中设置值
threadLocal.set("Hello, World!");
// 创建子线程并启动
Thread childThread = new Thread(() -> {
// 子线程中可以获取到父线程设置的值
String value = threadLocal.get();
System.out.println("Child Thread: " + value);
});
childThread.start();
// 在主线程中获取值
String value = threadLocal.get();
System.out.println("Main Thread: " + value);
}
}
运行上述代码,将会输出以下结果:
Child Thread: Hello, World!
Main Thread: Hello, World!
从输出可以看出,子线程能够获取到父线程设置的值,并且主线程也可以访问该值。
需要注意
的是,InheritableThreadLocal 类只能用于子线程从父线程继承值的场景,并不能实现父线程获取子线程的值。如果需要父线程获取子线程的值,可以考虑使用 ThreadLocal 类或其他线程间通信的机制。