ThreadLocal简介:
变量值的共享可以使用public static的形式,使所有线程都使用同一个变量;如果想实现每一个线程都有自己的共享变量该如何实现呢?JDK中的ThreadLocal类正是为了解决这样的问题。
ThreadLocal类并不是用来解决多线程环境下的共享变量问题,而是用来提供线程内部的共享变量,在多线程环境下,可以保证各个线程之间的变量互相隔离、相互独立。在线程中,可以通过get()/set()方法来访问变量。ThreadLocal实例通常来说都是private static类型的,它们希望将状态与线程进行关联。这种变量在线程的生命周期内起作用,可以减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
代码案例:
/**
* Java并发编程之ThreadLocal
* @author 尘世间迷途的小书童
*
*/
public class ThreadLocalTest extends Thread {
private static ThreadLocal<Integer> flag = new ThreadLocal<Integer>();
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for(int i=0; i<10; i++) {
if("B".equals(getName())) {
flag.set(100);
}else {
flag.set(i);
}
System.out.println(getName() + " threadLocal.get()= " + flag.get());
}
}
public static void main(String[] args) {
Thread threadA = new ThreadLocalTest();
threadA.setName("A");
Thread threadB = new ThreadLocalTest();
threadB.setName("B");
Thread threadC = new ThreadLocalTest();
threadC.setName("C");
threadA.start();
threadB.start();
threadC.start();
}
}
A threadLocal.get()= 0
B threadLocal.get()= 100
C threadLocal.get()= 0
B threadLocal.get()= 100
A threadLocal.get()= 1
B threadLocal.get()= 100
C threadLocal.get()= 1
B threadLocal.get()= 100
A threadLocal.get()= 2
B threadLocal.get()= 100
C threadLocal.get()= 2
B threadLocal.get()= 100
A threadLocal.get()= 3
B threadLocal.get()= 100
C threadLocal.get()= 3
B threadLocal.get()= 100
A threadLocal.get()= 4
B threadLocal.get()= 100
C threadLocal.get()= 4
B threadLocal.get()= 100
A threadLocal.get()= 5
C threadLocal.get()= 5
A threadLocal.get()= 6
C threadLocal.get()= 6
A threadLocal.get()= 7
C threadLocal.get()= 7
A threadLocal.get()= 8
C threadLocal.get()= 8
A threadLocal.get()= 9
C threadLocal.get()= 9
虽然两个线程都在向threadLocal对象中set()数据值,但每个线程都还是能取出自己设置的数据,确实可以达到隔离线程变量的效果。
ThreadLocal源码解析:
ThreadLocal常用方法介绍:
get()方法:获取与当前线程关联的ThreadLocal值。
set(T value)方法:设置与当前线程关联的ThreadLocal值。
initialValue()方法:设置与当前线程关联的ThreadLocal初始值。
当调用get()方法的时候,若是与当前线程关联的ThreadLocal值已经被设置过,则不会调用initialValue()方法;否则,会调用initialValue()方法来进行初始值的设置。通常initialValue()方法只会被调用一次,除非调用了remove()方法之后又调用get()方法,此时,与当前线程关联的ThreadLocal值处于没有设置过的状态(其状态体现在源码中,就是线程的ThreadLocalMap对象是否为null),initialValue()方法仍会被调用。
remove()方法:将与当前线程关联的ThreadLocal值删除。
今天突然发现以前用的不对,上面代码里的用法和run方法里创建一个变量有什么区别
public class ThreadLocalTest {
private static ThreadLocal<Integer> local = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
//给一个初始值不然累加会报错
return 0;
}
};
public static void main(String[] args) {
Thread[] thread = new Thread[10];
for(int i=0;i<thread.length;i++) {
thread[i] = new Thread(new Runnable() {
@Override
public void run() {
Integer count = local.get();
for(int a=0; a<thread.length; a++) {
count++;
}
local.set(count);
System.out.println(Thread.currentThread().getName() + " : " + local.get());
}
});
}
for(int i=0; i<thread.length; i++) {
thread[i].start();
}
}
}
实现原理:
根据线程id和线程对应的变量放入线程安全的Map,用的时候根据线程id去取值,存放值根据线程id去存;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ThreadLocalTest1 {
//线程安全的hashMap
private static Map<Long, Integer> local = new ConcurrentHashMap<>();
public static void main(String[] args) {
Thread[] thread = new Thread[10];
for(int i=0;i<thread.length;i++) {
thread[i] = new Thread(new Runnable() {
@Override
public void run() {
Long id = Thread.currentThread().getId();
//根据线程id获取值,没有值给个初始值
if(null == local.get(id)) {
local.put(id, 0);
}
Integer count = local.get(id);
for(int a=0; a<thread.length; a++) {
count++;
}
local.put(id, count);
System.out.println(Thread.currentThread().getName() + " : " + local.get(id));
}
});
}
for(int i=0; i<thread.length; i++) {
thread[i].start();
}
}
}