背景
threadlocal变量 是一种线程级全局的变量(普通的全局变量是进程级全局),就是在一个线程中,任何方法、函数能访问到这个threadlocal对象关联的对象,只要没有重新设置,都是同一个对象,变量作用域介于全局变量和局部变量之间,每个线程访问都是获取到threadlocal中对应得变量副本,线程之间互不干扰(线程安全情况下)。
1.全局变量(global variable),比如类的静态属性(加static关键字),在类的整个生命周期都有效;
2.局部变量(local variable),比如在一个方法中定义的变量,作用域只是在当前方法内,方法执行完毕后,变量就销毁(释放)了;
使用全局变量,当多个线程同时修改静态属性,就容易出现并发问题,导致脏数据;而局部变量一般来说不会出现并发问题(在方法中开启多线程并发修改局部变量,仍可能引起并发问题);
再看ThreadLocal,从名称上就能知道,它可以用来保存局部变量,只不过这个“局部”是指“线程”作用域,也就是说,该变量在该线程的整个生命周期中有效。
本质
threadlocal是一个数据结构和List Map等java数据结构地位是一样的,具体实现是每个“线程对象”中有一个Map,线程存活期中,这个MAP不会被释放,也不会被重置;
线程使用threadlocal时,首先要设置这个threadlocal,设置的时候,以threadlocal这个对象为KEY,以被设置的对象为value,存放到这个线程的MAP中,
后面的代码获取这个threadlocal对象的关联对象时,就以threadlocal对象为KEY 到当前线程的MAP中获取那个先前设置的对象,这样就能在线程中(不同函数中)共享这个被设置的对象了,实现跨函数访问能力。
虽然不同线程设置同一个threadlocal时,KEY是相同的,不过是用不同的MAP,而且访问的时候 也是到不同的MAP中找对象,所以只能访问到本线程设置的对象
说白了,就是各自玩各自得!不建议在线程池中用threadlocal,因为线程池得线程能重复使用不回收,这样可能就会导致脏数据。
例:
public class ThreadLocalTest {
ThreadLocal<String> lal = ThreadLocal.withInitial(()->"8888"); //第一种得默认初始化
ThreadLocal<String> local = new ThreadLocal<String>(){//第二种默认初始化
@Override
protected String initialValue() {
return "6666";
}
};
private String num;
private String word;
public String getWord() {
return lal.get();
}
public void setWord(String word) {
lal.set(word);
}
public String getNum() {
return local.get();
}
public void setNum(String num) {
local.set(num);
}
public static void main(String[] args){
ThreadLocalTest test = new ThreadLocalTest();
System.out.println("初始值--num----"+test.getNum());
System.out.println("初始值--word----"+test.getWord());
for (int i=0;i<50;i++) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
test.setNum(Thread.currentThread().getName()+"----set");
System.out.println("_____________");
System.out.println(Thread.currentThread().getName()+"_____"
+test.getNum());
}
});
t1.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ThreadLocal不能解决线程安全问题
例:
public class ThreadLocalDear { private static ThreadLocal<Integer> test = ThreadLocal.withInitial(()->0); public static Integer a = 0; public static Integer getA() { return a; } public static void setA(Integer a) { ThreadLocalDear.a = a; } public static void main(String[] args){ ThreadLocalDear dear = new ThreadLocalDear(); for (int i = 0; i < 10; i++) { new Thread() { public void run() { //1 a = test.get(); a++; //2 test.set(a); System.out.println("plus:" + Thread.currentThread().getName() + ": " + test.get()); } }.start(); } } }
输出:
plus:Thread-1: 1
plus:Thread-0: 1
plus:Thread-2: 1
plus:Thread-3: 1
plus:Thread-4: 1
plus:Thread-5: 1
plus:Thread-6: 1
plus:Thread-7: 1
plus:Thread-9: 1
plus:Thread-8: 1
在代码“1”的时候,每一个线程都会将threadLocal的初始值0赋值给共享变量a,因为每一个线程从threadLocal.get()拿到的值都是自己threadLocal保存的。所以,就没有所以了。
所以对于共享变量a来讲,每个线程都会首先将自己threadLocal里面的初始值0赋值给a,然后将共享变量a+1,然后将a+1的值设置到自己的ThreadLocalMap中,其他线程就访问不到了。下一个线程来的时候又会将自己threadLocal里面的初始值0赋值给a,然后将 a+1,然后... 如此周而复始。a只是被在0和1之间改来改去,最终放到每一个线程的threadLocal里面的a+1的值就不再共享。对于a这个共享变量来讲,如果在for循环创建的某线程A即将执行代码“2”之前,被其他for循环以外的某个线程改了一把。那么存到线程A的ThreadLocalMap中的值就是被改过的值了。
Java的ThreadLocal不是设计用来解决多线程安全问题的,事实证明也解决不了,共享变量a还是会被随意更改。ThreadLocal无能为力。所以,一般用ThreadLocal都不会将一个共享变量放到线程的ThreadLocal中。一般来讲,存放到ThreadLocal中的变量都是当前线程
本身就独一无二的一个变量。其他线程本身就不能访问,存到ThreadLocal中只是为了方便在程序中同一个线程之间传递这个变量。
所以,ThreadLocal和解决线程安全没有关系。
运用
1、登陆信息 session管理
2、数据库链接(链接一次,重复使用)