结构

image.png

适用场景

需要是当前线程资源持有的

  • 多个应用需要访问同一个线程持有的变量
  • 比如可以通过 ThreadLoalMap 拿用户数据

需要线程资源保持一致性

  • 比如 jdbc 控制同一个线程的事务中的数据库连接是同一个连接

image.png

线程安全

  • 避免线程不安全,因为 ThreadLocal 是当前线程才持有

分布式计算

  • 可以将需要计算的部分分分给多个线程计算,然后汇集计算结果

注意事项

  • tomcat 会重用线程
    • ThreadLocal 会被重用,如果是存储当前用户,需要先干掉

API

  • 简单使用

    1. class TL {
    2. public static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
    3. @Override
    4. protected Integer initialValue() {
    5. System.out.println("initial value!");
    6. return 1;
    7. }
    8. };
    9. public static void main(String[] args) {
    10. // 会初始化值
    11. local.get();
    12. // 不会初始化值
    13. local.get();
    14. // 干掉 local
    15. local.remove();
    16. // 会初始化值
    17. local.get();
    18. }
    19. }

减少同步/伪分布式

  • 比如访问接口的自增一计算
  • 如果要用 synchroinized 来修饰共享变量,会造成 qps 减低
    • 当然可以用 Atomic 类来搞,这里只是说一下如何减少同步
  • 可以统计各个线程的访问当前接口的访问量

image.png

  1. @RestController
  2. @RequestMapping("/lo")
  3. public class TreadLocalController {
  4. /**
  5. * 保存各个线程的 Val
  6. * 而各个 Val 保存是当前线程的 ThreadLocal 的值
  7. */
  8. static HashSet<Val<Integer>> saveSet = new HashSet<>();
  9. /**
  10. * 避免线程不安全
  11. * 由于在 initalValue() 中才会使用到,所以被执行的次数是 <线程总数>
  12. */
  13. private synchronized void setValue(Val<Integer> val) {
  14. saveSet.add(val);
  15. }
  16. /**
  17. * 当前线程 ThreadLocal
  18. */
  19. private ThreadLocal<Val<Integer>> threadLocal = new ThreadLocal<Val<Integer>>() {
  20. @Override
  21. protected Val<Integer> initialValue() {
  22. System.out.println(Thread.currentThread().getName() + "- 初始化了" );
  23. // 由于只能拿当前线程的值,干脆把数据存到另一个结构中,引用扔进 set
  24. Val<Integer> val = new Val<>();
  25. val.set(0);
  26. // HashSet 线程不安全
  27. setValue(val);
  28. return val;
  29. }
  30. };
  31. @RequestMapping("/add")
  32. public String add() {
  33. // 给 threadLocal 自增一
  34. Val<Integer> cur = threadLocal.get();
  35. cur.set(cur.get() + 1);
  36. return "success";
  37. }
  38. @RequestMapping("/get")
  39. public String get() {
  40. Integer sum = 0;
  41. for (Val<Integer> val : saveSet) {
  42. sum += val.get();
  43. }
  44. String str = "总数: " + saveSet.size() + ": 值: " + sum;
  45. return str;
  46. }
  47. }
  48. /**
  49. * 存储 ThreadLocal 的值,因为返回 ThreadLocal 本身有点难度
  50. * @param <T>
  51. */
  52. class Val<T> {
  53. private T t;
  54. public void set(T t) {
  55. this.t = t;
  56. }
  57. public T get() {
  58. return t;
  59. }
  60. }