ThreadLocal的使用方法

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的作用。

import java.util.ArrayList; import java.util.List; import java.util.Random; 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(); } }


运行结果

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


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

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; }

...

}


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值