ThreadLocal的使用方法

来源:互联网 发布:软件测试工作感想 编辑:程序博客网 时间:2024/06/14 22:11
ThreadLocal的含义是Thread Local Variable,它可以声明一个字段,使得不同的线程访问这个字段时,获取的都是当前线程的变量,互不影响。ThreadLocal的作用和在每个Thread类声明一个字段相同,那么什么时候使用它呢?还是在编写一些框架时,因为这时你无法预先定义Thread类。其中一个典型的用法是调用一个静态方法,这个静态方法会操作一个ThreadLocal变量,而不同的线程去调用时,访问的就是不同的副本。

下面通过这样一个例子来说明:模拟一个游戏,预先随机设定一个[1, 10]的整数,然后每个玩家去猜这个数字,每个玩家不知道其他玩家的猜测结果,看谁用最少的次数猜中这个数字。

这个游戏确实比较无聊,不过这里恰好可以把每个玩家作为一个线程,然后用ThreadLocal来记录玩家猜测的历史记录,这样就很容易理解ThreadLocal的作用。

Judge:用来设定目标数字以及判断猜测的结果。
Player:每个Player作为一个线程,多个Player并行地去尝试猜测,猜中时线程终止。
Attempt:具有ThreadLocal字段和猜测动作静态方法的类,ThreadLocal用于保存猜过的数字。
Record:保存历史记录的数据结构,有一个List<Integer>字段。

ThreadLocal为了可以兼容各种类型的数据,实际的内容是再通过set和get操作的对象,详见Attempt的getRecord()。

运行的时候,每个Player Thread都是去调用Attemp.guess()方法,进而操作同一个ThreadLocal变量history,但却可以保存每个线程自己的数据,这就是ThreadLocal的作用。

<span style="font-family:Courier New;">public class ThreadLocalTest {        public static void main(String[] args) {        Judge.prepare();        new Player(1).start();        new Player(2).start();        new Player(3).start();    }    }class Judge {        public static int MAX_VALUE = 10;    private static int targetValue;        public static void prepare() {        Random random = new Random();        targetValue = random.nextInt(MAX_VALUE) + 1;    }        public static boolean judge(int value) {        return value == targetValue;    }    }class Player extends Thread {        private int playerId;        public Player(int playerId) {        this.playerId = playerId;    }        @Override    public void run() {        boolean success = false;        while(!success) {            int value = Attempt.guess(Judge.MAX_VALUE);            success = Judge.judge(value);            System.out.println(String.format("Plyaer %s Attempts %s and %s", playerId, value, success ? " Success" : "Failed"));        }        Attempt.review(String.format("[IFNO] Plyaer %s Completed by ", playerId));    }    }class Attempt {        private static ThreadLocal<Record> history = new ThreadLocal<Record>();        public static int guess(int maxValue) {        Record record = getRecord();        Random random = new Random();        int value = 0;        do {            value = random.nextInt(maxValue) + 1;        } while (record.contains(value));        record.save(value);        return value;    }        public static void review(String info) {        System.out.println(info + getRecord());    }        private static Record getRecord() {        Record record = history.get();        if(record == null) {            record = new Record();            history.set(record);        }        return record;    }    }class Record {        private List<Integer> attemptList = new ArrayList<Integer>();;        public void save(int value) {        attemptList.add(value);    }        public boolean contains(int value) {        return attemptList.contains(value);    }        @Override    public String toString() {        StringBuffer buffer = new StringBuffer();        buffer.append(attemptList.size() + " Times: ");        int count = 1;        for(Integer attempt : attemptList) {            buffer.append(attempt);            if(count < attemptList.size()) {                buffer.append(", ");                count++;            }        }        return buffer.toString();    }    }</span>

运行结果:

Plyaer 1 Attempts 8 and Failed
Plyaer 1 Attempts 3 and Failed
Plyaer 1 Attempts 2 and  Success
[IFNO] Plyaer 1 Completed by 3 Times: 8, 3, 2
Plyaer 3 Attempts 5 and Failed
Plyaer 3 Attempts 8 and Failed
Plyaer 3 Attempts 7 and Failed
Plyaer 3 Attempts 1 and Failed
Plyaer 3 Attempts 3 and Failed
Plyaer 3 Attempts 10 and Failed
Plyaer 3 Attempts 2 and  Success
[IFNO] Plyaer 3 Completed by 7 Times: 5, 8, 7, 1, 3, 10, 2
Plyaer 2 Attempts 10 and Failed
Plyaer 2 Attempts 9 and Failed
Plyaer 2 Attempts 4 and Failed
Plyaer 2 Attempts 6 and Failed
Plyaer 2 Attempts 5 and Failed
Plyaer 2 Attempts 3 and Failed
Plyaer 2 Attempts 7 and Failed
Plyaer 2 Attempts 1 and Failed
Plyaer 2 Attempts 2 and  Success
[IFNO] Plyaer 2 Completed by 9 Times: 10, 9, 4, 6, 5, 3, 7, 1, 2

关于ThreadLocal的原理,可以从其get()方法的实现来看

<span style="font-family:Courier New;">public class ThreadLocal<T> {    ...    public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null)                return (T)e.value;        }        return setInitialValue();    }    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    ...}</span>

执行get()时首先获取当前的Thread,再获取Thread中的ThreadLocalMap - t.threadLocals,并以自身为key取出实际的value。于是可以看出,ThreadLocal的变量实际还是保存在Thread中的,容器是一个Map,Thread用到多少ThreadLocal变量,就会有多少以其为key的Entry。

总结

1、ThreadLocal是使用的一种线程的局部变量。

2、使用ThreadLocal可以很好每次都使用开始创建的那个对象。

3、Hibernate中就使用ThreadLocal来获取当前事务的处理。

4、ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

比如实际项目中我不想使用MVC框架,而使用自己简易线程安全的MVC框架,可以将request和response对象存放在ThreadLocal对象中,这样访问的就是已存在的request和response变量:

/** * 将request,response放到ThreadLocal方便普通java类调用 */public class MyCustomContext {private static ThreadLocal<HttpServletRequest> myRequest = new ThreadLocal<HttpServletRequest>();private static ThreadLocal<HttpServletResponse> myResponse = new ThreadLocal<HttpServletResponse>();/** * 获取当前登录用户 */public static  SysUserEntity getLoginUser() {return (SysUserEntity) getMySession().getAttribute("login_user");}/** * 将request、response放入ThreadLocal容器 */public static void setAll(HttpServletRequest request,HttpServletResponse response){myRequest.set(request);myResponse.set(response);}/** * 获得request * @return */public static HttpServletRequest getMyRequest(){return myRequest.get();}/** * 获得session */public static HttpSession getMySession(){HttpServletRequest request = getMyRequest();if(null == request){throw new MyRuntimeException("myRequest未赋值,无法获取session");}return request.getSession();}public static void setMyRequest(HttpServletRequest request){myRequest.set(request);}/** * 获得response */public static HttpServletResponse getMyResponse(){return myResponse.get();}public static void setMyRequest(HttpServletResponse response){myResponse.set(response);}public static void removeMyRequest(){myRequest.remove();}public static void removeMyResponse(){myResponse.remove();}public static void remoceAll(){myRequest.remove();myResponse.remove();}}




0 0
原创粉丝点击