当你学习使用一个陌生的关键字的时候,一定是记住他的使用情况,然后再去分析源码,最后再完完全全的掌握它,本文详细介绍一下ThreadLocal的使用案例
- 复制成员变量为线程本地变量
public class ThreadLocalTest {
//定义线程本地变量
private static final ThreadLocal<Foo> LOCAL_FOO = ThreadLocal.withInitial(() -> new Foo());
private static final ThreadPoolExecutor POOL_EXECUTOR =
new ThreadPoolExecutor(
10,
10,
20,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2000));
@Test
public void testThreadLocal() throws InterruptedException {
//共5个线程
for (int i = 0; i < 5; i++) {
POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
//获取“线程本地变量”中当前线程所绑定的值
if (LOCAL_FOO.get() == null) {
//设置“线程本地变量”中当前线程所绑定的值
System.out.println(Thread.currentThread().getName() + ":执行了set");
LOCAL_FOO.set(new Foo());
}
System.out.println(Thread.currentThread().getName() + ":初始的本地值:" + LOCAL_FOO.get());
//每个线程执行10次
for (int i = 0; i < 10; i++) {
Foo foo = LOCAL_FOO.get();
foo.setBar(foo.getBar() + 1);
LockSupport.parkNanos(10 * 1000L * 1000L);
}
System.out.println(Thread.currentThread().getName() + ":累加10次之后的本地值:" + LOCAL_FOO.get());
//删除“线程本地变量”中当前线程所绑定的值,对于线程池中的线程尤其重要
LOCAL_FOO.remove();
}
});
}
ThreadUtil.sleepMilliSeconds(Integer.MAX_VALUE);
}
@Data
static class Foo {
//实例总数
static final AtomicInteger AMOUNT = new AtomicInteger(0);
int index = 0; //对象的编号
int bar = 10; //对象的内容
//构造器
public Foo() {
index = AMOUNT.incrementAndGet(); //总数增加,并且给对象的编号
System.out.println(Thread.currentThread().getName() + "execute constructor index:"+ index );
}
@Override
public String toString() {
return index + "@Foo{bar=" + bar + '}';
}
}
}
总结:
static final修饰的成员变量并不会初始化,只有在调用get的时候才会init
- ThreadLocal修饰保存本地变量,在各个服务之间传递参数
public class ThreadLocalTest2 {
/** 模拟业务方法 */
public void serviceMethod() {
// 睡眠500ms,模拟执行耗时
LockSupport.parkNanos(500 * 1000L * 1000L);
// 记录开始调用到这个点p1的耗时
TimeLog.logPoint("point-1 service");
// 调用下一层方法:模拟dao业务方法
daoMethod();
// 调用下一层方法:模拟RPC远程业务方法
rpcMethod();
}
/** 模拟dao业务方法 */
public void daoMethod() {
// 睡眠400ms,模拟执行耗时
LockSupport.parkNanos(400 * 1000L * 1000L);
// 记录上一个点p1到这里p2的耗时
TimeLog.logPoint("point-2 dao");
}
/** 模拟RPC远程业务方法 */
public void rpcMethod() {
// 睡眠400ms,模拟执行耗时
LockSupport.parkNanos(600 * 1000L * 1000L);
// 记录上一个点p2到这里p3的耗时
TimeLog.logPoint("point-3 rpc");
}
/** 测试用例:线程方法调用的耗时 */
@org.junit.Test
public void testTimeLog() throws InterruptedException {
Runnable runnable =
() -> {
// 开始耗时记录
TimeLog.beginSpeedLog();
// 调用模拟业务方法
serviceMethod();
// 打印耗时
TimeLog.printCost();
// 结束耗时记录
TimeLog.endSpeedLog();
};
new Thread(runnable).start();
LockSupport.parkNanos(10 * 1000L * 1000L * 1000L); // 等待10s看结果
}
}
public class TimeLog {
/** 记录调用耗时的本地Map变量 */
private static final ThreadLocal<Map<String, Long>> TIME_RECORD_LOCAL =
ThreadLocal.withInitial(TimeLog::initialStartTime);
/** 记录调用耗时的本地Map变量的初始化方法 */
public static Map<String, Long> initialStartTime() {
Map<String, Long> map = new HashMap<>();
map.put("start", System.currentTimeMillis());
map.put("last", System.currentTimeMillis());
return map;
}
/** 开始耗时记录 */
public static final void beginSpeedLog() {
System.out.println("开始耗时记录");
TIME_RECORD_LOCAL.get();
}
/** 结束耗时记录 */
public static final void endSpeedLog() {
TIME_RECORD_LOCAL.remove();
System.out.println("结束耗时记录");
}
/** 记录方法的耗时 */
public static final void logPoint(String point) {
// 获取上一次的时间
Long last = TIME_RECORD_LOCAL.get().get("last");
// 计算耗时,并且保存
Long cost = System.currentTimeMillis() - last;
TIME_RECORD_LOCAL.get().put(point + " cost:", cost);
// 保存最近时间,供下一次使用
TIME_RECORD_LOCAL.get().put("last", System.currentTimeMillis());
}
/** print方法的耗时 */
public static final void printCost() {
Iterator<Map.Entry<String, Long>> it = TIME_RECORD_LOCAL.get().entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Long> entry = it.next();
System.out.println(Thread.currentThread().getName() + ":" + entry.getKey() + " =>" + entry.getValue());
}
}
}