Java常用类:ThreadLocal

作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)

ThreadLocal

ThreadLocal类主要是为了解决多线程并发,出现线程共用变量的问题。与synchronized等同步方法不同的是,synchronized是采取线程排队的方式,使得同一时间点只有一个线程访问非线程安全的变量;而ThreadLocal是为每个线程创建非线程安全变量的副本,线程之间无法交叉访问变量,从而解决并发问题。
ThreadLocal支持泛型变量,在类中初始化如下:

private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>()

1.几个重要的方法

//初始化方法,默认返回null
protected T initialValue() {
        return null;
    }
//set方法,给本地变量赋值,其实是用的ThreadLocalMap内部类来保存的
//将当前的线程对象作为key,传进来的value作为值
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
//获取本地变量
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
//移除当前的本地变量
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

主要就是以上四个方法,ThreadLocal通过ThreadLocalMap内部类,达到为每个线程创建一个变量的副本,使得每个线程之间互不干扰的访问非线程安全的变量。简单的理解就是ThreadLocal通过map以线程对象为key,变量值为value,线程只能修改获取它自己线程对应的变量。

2.简单例子

public class ThreadLocalDemo {
	//重写initialValue方法
    private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
	//获取的时候将值递增1
    public  Integer getValue(){
        threadLocal.set(threadLocal.get()+1);
        return threadLocal.get();
    }

    public static void main(String[] args) {
        ThreadDemo thread1 = new ThreadDemo(new ThreadLocalDemo());
        ThreadDemo thread2 = new ThreadDemo(new ThreadLocalDemo());
        ThreadDemo thread3 = new ThreadDemo(new ThreadLocalDemo());
        thread1.run();
        thread2.run();
        thread3.run();
    }

}

class ThreadDemo extends Thread{
    private ThreadLocalDemo threadLocalDemo;
    public ThreadDemo(ThreadLocalDemo threadLocalDemo){
        this.threadLocalDemo = threadLocalDemo;
    }

    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println(this.getName()+":"+this.threadLocalDemo.getValue());
        }
    }
}

结果:可以看出线程之间没有出现干扰的情况,变量都是从1递增到3,如果不使用ThreadLocal,那么三个线程同时访问一个变量就会出现并发的问题。

Thread-0:1
Thread-0:2
Thread-0:3
Thread-1:1
Thread-1:2
Thread-1:3
Thread-2:1
Thread-2:2
Thread-2:3

3.实际案例

在工作中也接触到了ThreadLocal,使用场景有在一次请求中保存用户信息;或者需要将日志记录到数据库中,每个请求的日志信息都不一样。

将用户信息保存到ThreadLocal中

在一次http请求中,会经过mvc三层,在这个过程中可能会在多个方法中需要获取用户信息,如果每次都读取数据库,会造成不必要的资源浪费。使用ThreadLocal能够达到一次读取,多次使用的效果。

//在请求中,验证用户信息无误后将用户信息存在ThreadLocal中,在这次请求
//直到返回结果的过程中,都可以通过getLoginUser()获取用户的信息。
public abstract class AbstractBaseController {

    private static final ThreadLocal<LoginUserEntity> loginUserContainer = new ThreadLocal<>();

    /**
     * 初始化request和response对象
     */
    @ModelAttribute
    private final void initRequestAndResponse() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (Objects.nonNull(authentication)) {
            Object principal = authentication.getPrincipal();
            if (principal instanceof LoginUserEntity) {
                loginUserContainer.set((LoginUserEntity)principal);
            }

        }
    }

    /**
     * 获取当前线程的登录用户user对象
     *
     * @return LoginUserEntity
     */
    protected final LoginUserEntity getLoginUser() {
        return loginUserContainer.get();
    }

}

总结:ThreadLocal相比其他多线程解决方案来说,比较适合于变量隔离的场景,如果是要对一个值进行累加或累减再存回数据库,如秒杀的库存等,就不太适合使用ThreadLocal,那种业务场景还是适合使用同步来解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值