首先看以下代码:
public class Main {
public static class MyRunnable implements Runnable {
int val = 0;
@Override
public void run() {
val = (int) (Math.random()*100);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(val);
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
}
}
/*
可能的输出:65 , 65
*/
从以上代码我们可以发现 , 变量var是被thread1和thread2两个线程共享的 , 当其中一个线程修改了var值的话另外一个线程也修改var那么此时会造成var值被覆盖掉.
这是一个典型的多个线程跑同一份代码。但是,如果我们不想这些线程共享某些变量,或者说var在每个线程内都有一个独立备份,这时候该怎么办呢?TreadLocal就是用来解决这个问题的。
Demo1:
public class Main {
public static class MyRunnable implements Runnable {
private ThreadLocal threadLocal = new ThreadLocal();
@Override
public void run() {
threadLocal.set((int) (Math.random() * 100D));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
}
}
/*
可能的输出:68 , 34
*/
此时我们就拥有一个变量的独立备份, 假设咱们需要两个或者三个或者更多呢? 其实很简单 只需要再添加一个ThreadLocal对象用来存储数据就行了 , 也就是说一个ThreadLocal只代表一个变量的独立备份,多个变量需要独立备份,就要有多个ThreadLocal对象来存放独立备份.
以下代码就能完成两个不共享的变量:
public class Main {
public static class MyRunnable implements Runnable {
private ThreadLocal threadLocal = new ThreadLocal();
private ThreadLocal threadLocal2 = new ThreadLocal();
@Override
public void run() {
threadLocal.set((int) (Math.random() * 100D));
threadLocal2.set(Math.random() * 100D);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
System.out.println(threadLocal2.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
}
}
/*
可能的输出:28 , 34 , 56.049492341729 , 11.603018021238775
*/
JDK中如何实现
JDK中的实现 , 在Thread类中,有一个ThreadLocal.ThreadLocalMap类型的变量threadLocals,实现类似上面container的功能;其次,在ThreadLocal的get和set方法内部获取threadLocals来存放数据。threadLocals也不像上面的container,直接使用字符串做为键,而是使用ThreadLocal对象本身,不同的ThreadLocal对象刚好对应这不同的独立备份。
ThreadLocal如何防止内存泄漏
ThreadLocal中,获取到线程私有对象是通过Thread类持有的一个threadLocalMap(threadLocals),然后传入ThreadLocal当做key获取到对象的,这时候就有个问题,如果你在使用完ThreadLocal之后,将其置为null,这时候这个对象并不能被回收,因为他还有 ThreadLocalMap->entry->key的引用,直到该线程被销毁,但是这个线程很可能会被放到线程池中不会被销毁,这就产生了内存泄露,jdk是通过弱引用来解决的这个问题的,entry中对key的引用是弱引用,当你取消了ThreadLocal的强引用之后,他就只剩下一个弱引用了,所以也会被回收。
可参考文章:
https://blog.csdn.net/u011983531/article/details/50833784