前言
ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实例的时候(通过其get() 和 set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户ID或事务ID)与线程关联起来。
是什么:
实现每一个线程都有自己专属的本地变量副本( 自己用自己的变量不麻烦别人,不和其他人共享,人人有份,人各一份。
主要解决了让每个线程绑定自己的值,通过使用get和set方法,获取默认值或将其值更改为当前线程所在的副本的值从而避免了线程安全问题。
栗子:
比如 每一个人都有单独的 血槽, 装备。
API介绍
示例代码
package com.flamKlin.juc;
import java.util.Random;
/**
* 1.saleHouse方法 是对于 5个销售卖房子,集团高层只关心 销售总量的准确 统计数量
* 2.saleHouseThreadLocal 方法 是 对于 五个 销售 卖完方法 各自所拿到的提成
*/
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
House house = new House();
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
int size = new Random().nextInt(5) + 1;
try {
for (int j = 1; j <= size; j++) {
house.saleHouse();
house.saleHouseThreadLocal();
}
System.out.println(Thread.currentThread().getName() + "\t :号销售卖出" + house.saleVolume.get());
} finally {
house.saleVolume.remove();
}
}).start();
}
Thread.sleep(100);
System.out.println("总共卖出了 :" + house.saleCount);
}
}
class House {
int saleCount = 0;
// 初始化赋值
// ThreadLocal<Integer> saleVolume = new ThreadLocal<Integer>(){
// @Override
// protected Integer initialValue() {
// return 0;
// }
// };
ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
// 对于 多个线程操作 同一个共享变量操作 使用加锁
public synchronized void saleHouse() {
++this.saleCount;
}
//对于 多个线程
public void saleHouseThreadLocal() {
saleVolume.set(1 + saleVolume.get());
}
}
输出结果
编码规则 引申出使用 remove 方法 (阿里编码规约)
【强制】必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用, 如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。
尽量在代理中使用 try-finally 块进行回收。
正例:
objectThreadLocal.set(userInfo);
try {
// …
} finally {
objectThreadLocal.remove();
}
package com.flamKlin.juc;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
*/
public class ThreadLocalPoolDemo {
public static void main(String[] args) {
MyData myData = new MyData();
// 为了案例方便 不适用 自定义线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
try {
for (int i = 0; i < 5; i++) {
threadPool.submit(() -> {
try {
Integer beforeInt = myData.threadLocal.get();
myData.add();
Integer afterInt = myData.threadLocal.get();
System.out.println(Thread.currentThread().getName() + "\t beforeInt :" + beforeInt + "\t afterInt :" + afterInt);
} finally {
myData.threadLocal.remove(); // 不加会使得 以及操作过的线程, 再次拿到数值进行add 操作
}
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
class MyData{
ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public void add(){
threadLocal.set(1 + threadLocal.get());
}
}