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。
转自:http://gaofeihang.blog.163.com/blog/static/84508285201293071940706/

ThreadLocal的使用方法相关推荐

  1. ThreadLocal的重要方法介绍

    ThreadLocal主要方法 protected T initialValue():初始化 该方法会返回当前线程对应的"初始值",这是一个延迟加载的方法,只有在调用get的时候才 ...

  2. threadlocal的set()方法中的内存回收

    ThreadLocal在执行set()方法的时候,实际执行set()逻辑的是其内部类ThreadLocalMap. private void set(ThreadLocal<?> key, ...

  3. 全方位,多角度理解ThreadLocal

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://blog.csdn.net/zzg1229059735/article/details/82715741 本次 ...

  4. 为何每次用完 ThreadLocal 都要调用 remove()

    点击上方蓝色"方志朋",选择"设为星标"回复"666"获取独家整理的学习资料! 什么是内存泄漏 Key 的泄漏 Value 的泄漏 如何避免 ...

  5. 这玩意比ThreadLocal叼多了,吓得我赶紧分享出来。

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! Dubbo的一次提交开始 故事得从前段时间翻阅 Dubbo ...

  6. 优雅的使用 ThreadLocal

    前言 在我们日常 Web 开发中难免遇到需要把一个参数层层的传递到最内层,然后中间层根本不需要使用这个参数,或者是仅仅在特定的工具类中使用,这样我们完全没有必要在每一个方法里面都传递这样一个 通用的参 ...

  7. Java并发编程:线程封闭和ThreadLocal详解

    什么是线程封闭 当访问共享变量时,往往需要加锁来保证数据同步.一种避免使用同步的方式就是不共享数据.如果仅在单线程中访问数据,就不需要同步了.这种技术称为线程封闭.在Java语言中,提供了一些类库和机 ...

  8. 深入浅出ThreadLocal,你会吗?

    ThreadLocal全面解析 学习目标 了解ThreadLocal的介绍 掌握ThreadLocal的运用场景 了解ThreadLocal的内部结构 了解ThreadLocal的核心方法源码 了解T ...

  9. ThreadLocal的正确使用与原理

    ThreadLocal是什么 ThreadLocal是线程Thread中属性threadLocals即ThreadLocal.ThreadLocalMap的管理者,ThreadLocal用于给每个线程 ...

最新文章

  1. 海思3536:PC客户端编译过程报错及解决方法
  2. 武汉大专计算机专业分数线,武汉高考分数线最低的大专,2021年武汉大专最低分数线是多少...
  3. Delphi面向对象编程的20条规则
  4. android分析windowManager、window、viewGroup之间关系
  5. django项目学习之QQ登录
  6. ubuntu 安装 TensorFlow、opencv3 的 tips
  7. mui 头部tab代码
  8. 【直通顶会 ICCV2021比赛大揭秘】ICCV2021比赛信息汇总
  9. angular中如何定义全局变量_如何在Angular 2 / Typescript中声明全局变量?
  10. JPA基础(四):第一个JPA实例与JPA主键生成策略
  11. u-boot移植随笔:System.map文件格式
  12. 计算机的组成 —— 主板
  13. AUTOCAD——添加自定义填充图案
  14. python建模的步骤_python基础教程之Python 建模步骤|python基础教程|python入门|python教程...
  15. 【论文笔记】(FGSM公式推导)Explaining and Harnessing Adversarial Examples
  16. 永远不要使用 Boolean 对象
  17. 利用backtrace和ucontex定位segment错误【转】
  18. 硬盘数据恢复原理与方法(一)
  19. 正方教务系统成绩爬虫的实现
  20. Tomcat单机多实例配置

热门文章

  1. String、StringBuffer与StringBuilder之间区别 (转载)
  2. \\s+ split替换
  3. android学习笔记之十服务(Service)
  4. 【OSX】build AOSP 2.3.7时的build error解决
  5. 求1 + 2 + 3...+ n的和
  6. .NET工程师必须掌握的知识点
  7. [转载]ESFramework 4.0 快速上手(15) -- 客户端登录验证
  8. linux下如何查看某个软件 是否安装??? 安装路径在哪???
  9. Python_赋值和深浅copy
  10. github上fork了别人的项目后,再同步更新别人的提交