• 廖雪峰Java11多线程编程-4线程工具类-1ThreadLocal


    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结构
  • 相关阅读:
    uva 532
    uva 10557
    uva 705
    uva 784
    uva 657
    uva 572
    uva 10562
    usa物价统计
    2019/6/30,道歉书
    名词收集
  • 原文地址:https://www.cnblogs.com/csj2018/p/11047825.html
Copyright © 2020-2023  润新知