Java多线程——2 ThreadLocal

来源:互联网 发布:centos开启snmp 编辑:程序博客网 时间:2024/06/13 04:15
在多线程的问题上对于线程不安全的变量,又不想采用阻塞线程的编程模式写代码,这是我们可以考虑采用ThreadLocal,给每个线程复制一个变量值的副本,使得不安全变量变得安全起来。·


1、线程局部变量,它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
2、ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户ID或事务ID)相关联
3、使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。同时请注意,如果是static的ThreadLocal,一般不需要调用remove。
4、通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map(ThreadLocalMap)中,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。 
5、如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
6、ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点: 
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。 
7、ThreadLocal类主要是作为工具类来使用,那么ThreadLocal.set()进去的对象是放在哪儿的呢? 

set()源代码:

Thread t = Thread.currentThread();        ThreadLocalMap map = t.threadLocals;        if (map != null)            map.set(this, value);        else            createMap(t, value);

可看到ThreadLocal也就是以线程为key,将变量副本当作value值与当前线程绑定起来而已。
而get()也是同样的道理
ThreadLocalMap 类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中


8、下面举一个小例子
User类

public class User{private static ThreadLocal<String> state = new ThreadLocal<String>();public String getState(){return state.get();}public void setState(String s){state.set(s);}}

TestThread类

public class TestThread extends Thread{private User u;public TestThread(String threadName, User u){super(threadName);this.u = u;}public void run(){u.setState("处于"+this.getName()+"状态良好");for(int i=0; i<3; i++){System.out.println(this.getName()+": "+u.getState());try{Thread.currentThread().sleep(1000);}catch (InterruptedException e){// TODO Auto-generated catch blocke.printStackTrace();}}}}

Main类

public class Main{public static void main(String[] args){User user = new User();TestThread thread1 = new TestThread("thread1",user);TestThread thread2 = new TestThread("thread2",user);TestThread thread3 = new TestThread("thread3",user);thread1.start();thread2.start();thread3.start();}}

运行结果:


thread1: 处于thread1状态良好
thread3: 处于thread3状态良好
thread2: 处于thread2状态良好
thread3: 处于thread3状态良好
thread2: 处于thread2状态良好
thread1: 处于thread1状态良好
thread3: 处于thread3状态良好
thread1: 处于thread1状态良好
thread2: 处于thread2状态良好


可见虽然是同一个User变量,可是在都附上了与线程相关联的state变量,User的state变量也变得线程安全了,其他线程无法访问到该线程的变量了。