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结构

廖雪峰Java11多线程编程-4线程工具类-1ThreadLocal的更多相关文章

  1. 廖雪峰Java11多线程编程-2线程同步-3死锁

    1.线程锁可以嵌套 在多线程编程中,要执行synchronized块: 必须首先获得指定对象的锁 Java的线程锁是可重入的锁.对同一个对象,同一个线程,可以多次获取他的锁,即同一把锁可以嵌套.如以下 ...

  2. 廖雪峰Java11多线程编程-2线程同步-2synchronized方法

    1.Java使用synchronized对一个方法进行加锁 class Counter{ int count = 0; public synchronized void add(int n){ cou ...

  3. 廖雪峰Java11多线程编程-1线程的概念-1多线程简介

    多任务 现代操作系统(windows,MacOS,Linux)都可以执行多任务: 多任务就是同时运行多个任务,例如同时开启钉钉.百度网盘.火狐.谷歌.ps等 操作系统执行多任务就是让多个任务交替执行, ...

  4. 廖雪峰Java11多线程编程-2线程同步-4wait和notify

    wait和notify synchronized解决了多线程竞争的问题 我们可以在synchronized块中安全的对一个变量进行修改,但是它没有解决多线程协调的问题. 例如设计一个TaskQueue ...

  5. 廖雪峰Java11多线程编程-2线程同步-1同步代码块

    1.线程安全问题 多个线程同时运行,线程调度由操作系统决定,程序本身无法决定 如果多个线程同时读写共享变量,就可能出现问题 class AddThread extends Thread{ public ...

  6. 廖雪峰Java11多线程编程-1线程的概念-2创建新线程

    Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...

  7. 廖雪峰Java11多线程编程-1线程的概念-5中断线程

    1.中断线程: 如果线程需要执行一个长时间任务,就可能需要中断线程.场景:从网络上下载一个100M的文件,用户在下载过程中中断下载任务的执行. 中断线程就是其他线程给该线程发一个信号,该线程收到信号后 ...

  8. 廖雪峰Java11多线程编程-1线程的概念-3线程的状态

    1线程的状态 线程终止的的原因: run()或call()方法执行完成,线程正常结束 线程抛出一个未捕获的Exception或Error 直接调用该线程的stop()方法来结束该线程--该方法容易导致 ...

  9. 廖雪峰Java11多线程编程-3高级concurrent包-4Concurrent集合

    Concurrent 用ReentrantLock+Condition实现Blocking Queue. Blocking Queue:当一个线程调用getTask()时,该方法内部可能让给线程进入等 ...

随机推荐

  1. [转]设置修改CentOS系统时区

    在我们使用CentOS系统的时候,也许时区经常会出现问题,有时候改完之后还是会出错,下面我们就来学习一种方法来改变这个状况.如果没有安装,而你使用的是 CentOS系统 那使用命令 yum insta ...

  2. Metasploit 使用MSFconsole接口

    什么是MSFconsole? 该msfconsole可能是最常用的接口使用Metasploit框架(MSF).它提供了一个“一体化”集中控制台,并允许您高效访问MSF中可用的所有选项.MSFconso ...

  3. tensorflow TypeError: Can not convert a float32 into a Tensor or Operation

    遇到这种情况可能是你的程序中有和你定义的tensor 变量重名的其他变量名字,jishi在for循环中使用了这个名字的作为临时变量也不行.tenor 变量很娇气.坑了我一晚上的时间. 比如:x = t ...

  4. 创建第一个spirngmvc小项目

    题外: 设置目录为源代码目录 1.进入:file->project structure->modules->soures 进入这个里面,选择相应的文件夹.例如src/java里的ja ...

  5. 2014 0416 word清楚项目黑点 输入矩阵 普通继承和虚继承 函数指针实现多态 强弱类型语言

    1.word 如何清除项目黑点 选中文字区域,选择开始->样式->全部清除 2.公式编辑器输入矩阵 先输入方括号,接着选择格式->中间对齐,然后点下面红色框里的东西,组后输入数据   ...

  6. 20140307 引用赋值、类的初始化、指针数组、数组指针、new

    引用不能被赋值http://blog.csdn.net/laixingjun/article/details/9005200 类构造函数两种初始化方法区别,哪种好:http://blog.163.co ...

  7. tcp_tw_recycle和tcp_timestamps的一些知识(转)

    现在很多公司都用LVS做负载均衡,通常是前面一台LVS,后面多台后端服务器,这其实就是NAT,当请求到达LVS后,它修改地址数据后便转发给后端服务器,但不会修改时间戳数据,对于后端服务器来说,请求的源 ...

  8. pytong下安装安装SK-Learn

    安装SK-Learn需要依赖的Python安装包有: Python (>= 2.6), NumPy (>= 1.3), SciPy (>= 0.7), 下载python的各种包的地址 ...

  9. JDBC_入门及注入问题

    .JDBC基本概念: java database Connectivity java数据库连接,java语言操作数据库 本质: 官方定义的一套操作所有关系型数据库的规则,即接口. 各个数据库厂商实现这 ...

  10. Mybatis使用Mapper方式CURD

    Mybatis 使用Dao代码方式进行增.删.改.查和分页查询. 1.Maven的pom.xml 2.配置文件 2.1.db.properties 2.2.mybatis.xml <?xml v ...