1. ThreadLocal

1.1 回顾

多线程是Java实现多任务的基础:

  • Thread:通过Thread来启动一个新的线程。Thread对象代表一个线程:调用Tread.currentThread()获取当前线程。
  • ExecutorService、ScheduledThreadPool、Fork/Join:通过这些多线程框架实现多任务

多任务程序通常需要针对每个任务启动一个新的线程。

<img src="https://img2018.cnblogs.com/blog/1418970/201906/1418970-20190618213937414-2031620910.png" width=500" />

对于每个访问web程序的用户,我们都会启动一个新的线程,来处理这个用户的请求。当然也可以从线程池取出一个空闲的线程来处理。

1.2 ThreadLocal

问题:如何在一个线程内传递状态?

例如我们在一个线程处理过程中,经常需要调用不同的类来处理不同的功能,我们如何在这些方法中能够方便的获取到当前的用户?

JDK提供了ThreadLocal,在一个线程中传递同一个对象。

    static ThreadLocal<String> threadLocalUser = new ThreadLocal<>();
threadLocalUser.set("Bob"); //给当前线程绑定指定的值
...
String current = threadLocalUser.get(); //调用get()方法可以 随时获取 当前线程已绑定的值
String current = threadLocalUser.get();
String current = threadLocalUser.get();
...
threadLocalUser.remove(); //把绑定的值从当前线程中解除

ThreadLocal典型的使用方式:



方法调用一定是同一个线程执行的,所以step1和printUser内部获取的User对象是同一个对象。

不同的线程关联的User是不同的对象,所以可以通过ThreadLocal来传递同一个对象。

便于理解,可以把ThreadLocal看成全局Map<Thread, Object>:每个线程获取ThreadLocal变量时,使用Thread自身作为key

    Object ThreadLocalValue = threadLocalMap.get(Thread.currentThread());

1.3 清除ThreadLocal

注意:ThreadLocal一定要在finally中清除。

这是当前线程执行完相关代码以后,很有可能重新放入线程池中。

如果ThreadLocal没有被清除,这个线程在执行其他代码的时候,就会把上一次的状态带进去。

    try{
UserContext.set(user);
...
}finally{
UserContext.remove();
}

2.示例

class User{ //表示当前的一个用户
String name;
int level;
public User(String name, int level){
this.name = name;
this.level = level;
}
}
class UserContext implements AutoCloseable{
static final ThreadLocal<User> context = new ThreadLocal<>(); //全局唯一静态变量
public static User getCurrentUser(){ //获取当前线程的ThreadLocal User
return context.get();
}
public UserContext(User user){ //初始化ThreadLocal的User
context.set(user);
}
@Override
public void close(){ //移除ThreadLocal关联的User
context.remove();
}
}
class ProcessThread extends Thread{
User user;
ProcessThread(User user){ //传入User对象
this.user = user;
}
public void run(){
try(UserContext ctx = new UserContext(user)){
//hello和checkLevel是没有参数的方法,但是我们仍然可以在方法内部获取到线程对象关联的User。
new Greeting().hello();
Level.checkLevel();
}
}
}
class Greeting{
void hello(){
User user = UserContext.getCurrentUser();
System.out.println("Hello,"+user.name+"!");
}
}
class Level{
static void checkLevel(){
User user = UserContext.getCurrentUser();
if(user.level>100){
System.out.println(user.name+" is a VIP user.");
}else{
System.out.println(user.name+" is a registered user.");
}
}
}
public class Main{
public static void main(String[] args) throws Exception{
Thread t1 = new ProcessThread(new User("Bob",120));
Thread t2 = new ProcessThread(new User("Alice",80));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Main end");
}
}

3.总结:

  • ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的
  • ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递)
  • 使用ThreadLocal要用try..finally结构

最新文章

  1. POJ 1753. Flip Game 枚举or爆搜+位压缩,或者高斯消元法
  2. 【面试】shuffle函数的实现
  3. python学习之深入
  4. 基于cookie实现zTree树刷新后,展开状态不变
  5. Android 调用系统照相机拍照和录像
  6. SphinxSE 一些SQL查询语句
  7. 写好的mapreduce程序,编译,打包,得到最后的jar包! 验证jar包 ! 整体流程
  8. Android中ListView滚动时上下边界的那一抹色彩
  9. sql shard/partition
  10. smarty中的变量使用
  11. [WebKit]浏览器的加载与页面性能优化
  12. oracle数据库敏感操作前创建还原点
  13. JS中小数的差,比较大小
  14. Dapper.Contrib拓展及数据库生成实体
  15. 实践详细篇-Windows下使用VS2015编译的Caffe训练mnist数据集
  16. DataStructure-链表实现指数非递减一元多项式的求和
  17. java io系列24之 BufferedWriter(字符缓冲输出流)
  18. RSA 非对称加密,私钥转码为pkcs8 错误总结
  19. 一个酷绚的linux 桌面程序 GLX-DOCK (cario-dock)
  20. Vue.js 响应式原理

热门文章

  1. 20140402 cmake编译错误原因 同时装了vs2010和vs2012
  2. 初学hibernate的心得体会
  3. 为kubectl配置别名和命令行补齐
  4. SpringCloud学习笔记《---06 Config 分布式配置中心---》基础篇
  5. Aop 简单实例
  6. Linux 常用命令:文本查看篇
  7. Python自学:第五章 使用range( )创建数字列表
  8. [JZOJ3302] 【集训队互测2013】供电网络
  9. 「STL」bitset正传
  10. JSOI 2008 魔兽地图