1. ThreadLocal 定义
-
定义
提供线程局部变量;一个线程线程局部变量在多个线程中,分别有独立的值(副本)。
-
特点
简单(开箱即用)、快速(无额外开销)、安全(线程安全)。
-
场景
多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)。
-
实现原理
Java中用哈希表实现。
-
应用范围
几乎所有提供多线程特征的语言。
2. ThreadLocal 基本API
public class ThreadLocalTest {
public static ThreadLocal<Long> x = new ThreadLocal<Long>(){
@Override
protected Long initialValue() {
System.out.println(Thread.currentThread().getId() + " Initial value run...");
return Thread.currentThread().getId();
}
};
public static void main(String[] args) {
new Thread(()->{
System.out.println(x.get());
}).start();
x.set(100L);
//x.remove();
System.out.println(x.get());
}
}
3. ThreadLocal 的4种关键场景
-
线程资源持有
持有线程资源供线程的各个部分使用,全局获取,减少编程难度。
-
线程资源一致性
帮助需要保持线程一致的资源(如数据库事务)维护一致性,降低编程难度。
-
线程安全
帮助只考虑了单线程的程序库,无缝向多线程场景迁移。
-
分布式计算
帮助分布式计算场景的各个线程累积局部计算结果。
4. ThreadLocal 并发场景分析
-
Apache ab 压力并发测试工具的使用
-
用synchronize实现线程安全的缺点
性能得不到满足。
-
用ThreadLocal实现并发线程安全
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class StatController { //static Integer c =0; static ThreadLocal<Integer> c = new ThreadLocal<Integer>(){ @Override protected Integer initialValue(){ return 0; } }; void _add(){ try { Thread.sleep(100); c.set(c.get() + 1); } catch (InterruptedException e) { e.printStackTrace(); } } @RequestMapping("/stat") public Integer stat(){ return c.get(); } @RequestMapping("/add") public Integer add(){ _add(); return 1; } }
上面的代码是有问题的,因为获取的值是每个线程自己的变量值,每次累加的也是每个线程自己的累加值。
正确的写法应该是:
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashSet; @RestController public class StatController { static HashSet<Val<Integer>> set = new HashSet<Val<Integer>>(); synchronized static void addSet(Val<Integer> v){ set.add(v); } static ThreadLocal<Val<Integer>> c = new ThreadLocal<Val<Integer>>(){ @Override protected Val<Integer> initialValue(){ Val<Integer> v = new Val<>(); v.setV(0); addSet(v); return v; } }; void _add(){ try { Thread.sleep(100); Val<Integer> v = c.get(); v.setV(v.getV() + 1); } catch (InterruptedException e) { e.printStackTrace(); } } @RequestMapping("/stat") public Integer stat(){ return set.stream().map(x-> x.getV()).reduce((a,x)-> a+x).get(); } @RequestMapping("/add") public Integer add(){ _add(); return 1; } }
public class Val<T> { T v; public T getV() { return v; } public void setV(T v) { this.v = v; } }