美文网首页
介绍ThreadLocal

介绍ThreadLocal

作者: 大风过岗 | 来源:发表于2020-06-09 17:15 被阅读0次

1、概览

在本文中,我们将研究下ThreadLocal。ThreadLocal为我们提供了为当前线程单独保存数据的能力,并把它包装在一个特殊的对象类型里面。

2、ThreadLocal 的API

ThreadLocal这种结构体使得我们可以存储一些只能被特定线程访问的数据。
比如说,我们想存储一个Integer值,这个Integer值将和某个特定的线程绑定在一起。

ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();

当我们在某个线程中,要使用该值的时候,我们只需要调用get()或set()方法即可。简单点说,我们可以认为ThreadLocal是把数据存储到了一个Map中了,而键就是当前线程。

正是基于这样事实,当我们调用threadLocalValue的get()方法时,我们获取到的是所请求线程的ThreadLocal中存储的值。

threadLocalValue.set(1);
Integer result = threadLocalValue.get();

使用withInitial()这个静态方法并传递一个初始值,我们可以直接构造一个ThreadLocal实例。

要移除ThreadLocal中的值,我们可以调用remove方法:

threadLocal.remove();

要想知道如何正确地使用ThreadLocal,首先,我们先看一个例子,在这个例子中,我们先不使用ThreadLocal,之后,我们重写此案例,并利用ThreadLocal改写它。

3、把用户数据存储在Map中

我们设想有一个程序,它需要为每个给定的用户,存储该用户特定的上下文数据。

public class Context {
    private String userName;

    public Context(String userName) {
        this.userName = userName;
    }
}

我们想让每个用户都有一个线程。我们创建一个SharedMapWithUserContext类,这个类实现了Runnable接口。
在run方法的实现中,会调用UserRepository查询给定用户的上下文信息。

下一步,我们就把上下文数据存储到ConcurrentHashMap中。

public class SharedMapWithUserContext implements Runnable {

    public static Map<Integer, Context> userContextPerUserId
      = new ConcurrentHashMap<>();
    private Integer userId;
    private UserRepository userRepository = new UserRepository();

    @Override
    public void run() {
        String userName = userRepository.getUserNameForUserId(userId);
        userContextPerUserId.put(userId, new Context(userName));
    }

    // standard constructor
}

要测试我们的程序,很容易。我们只需要为俩个不同的userId创建俩个线程,并判断userConotextPerUserId中有俩个entry。

SharedMapWithUserContext firstUser = new SharedMapWithUserContext(1);
SharedMapWithUserContext secondUser = new SharedMapWithUserContext(2);
new Thread(firstUser).start();
new Thread(secondUser).start();

assertEquals(SharedMapWithUserContext.userContextPerUserId.size(), 2);

4、把用户数据存储在ThreadLocal中

我们可以重写上面的案例,把用户的Context实例存储在ThreadLocal中。每一个线程都有它自己的ThreadLocal实例。

在使用ThreadLocal时,我们需要额外的小心。因为每一个ThreadLocal实例都和一个特定的线程相关。在我们的案例中,每一个userId都有一个专门的线程。run()方法会获取用户的上下文,并把它存储在ThreadLocal中。


public class ThreadLocalWithUserContext implements Runnable {

    private static ThreadLocal<Context> userContext
      = new ThreadLocal<>();
    private Integer userId;
    private UserRepository userRepository = new UserRepository();

    @Override
    public void run() {
        String userName = userRepository.getUserNameForUserId(userId);
        userContext.set(new Context(userName));
        System.out.println("thread context for given userId: "
          + userId + " is: " + userContext.get());
    }

    // standard constructor
}

我们启动来个线程,来检查程序的执行情况。

ThreadLocalWithUserContext firstUser
  = new ThreadLocalWithUserContext(1);
ThreadLocalWithUserContext secondUser
  = new ThreadLocalWithUserContext(2);
new Thread(firstUser).start();
new Thread(secondUser).start();

在运行完值,我们将在控制台看到如下输出:

thread context for given userId: 1 is: Context{userNameSecret='18a78f8e-24d2-4abf-91d6-79eaa198123f'}
thread context for given userId: 2 is: Context{userNameSecret='e19f6a0a-253e-423e-8b2b-bca1f471ae5c'}

可以看到,每一个用户都有了一个它自己的上下文。

5、不要把ThreadLocal和ExecutorService一块使用

如果我们想使用ExecutorService,并提交一个Runnable给它。使用ThreadLocal的话,可能产生不确定结果。因为我们无法保证,指定用户的Runnalbe行为在每次执行时,都是由相同的线程运行的。

由于这个原因,ThreadLocal就可能会在不同的userId间共享。这就是为什么我们不能把ThreadLocal和ExecutorService一起使用的原因。只有当我们可以完全控制,由哪一个线程运行我们的任务时,我们才能使用ThreadLocal。

6、总结

在这篇文章中,我们看了ThreadLocal的用法。我们先是使用在多个线程间共享的ConcurrentHashMap来存储特定用户的上下文数据。之后,我们重写了该案例,并使用ThreadLocal来存储数据,把特定的userId存储在特定线程的ThreadLocal中。

相关文章

  • 大话 ThreadLocal

    概述 ThreadLocal 介绍 ThreadLocal 关键方法讲解 ThreadLocalMap 内部类介绍...

  • ThreadLocal原理介绍以及内存泄漏分析

    ThreadLocal简单介绍 ThreadLocal同ReentrantLock,CyclicBarrier等都...

  • Looper场景ThreadLocal原理分析

    ThreadLocal介绍 ThreadLocal支持泛型,ThreadLocal,T代表的是线程本地变量,...

  • 聊一聊我眼中的ThreadLocal(面试题形式总结)

    这篇总结一下 ThreadLocal,主要的议题有: ThreadLocal 介绍 ThreadLocal 实现原...

  • ThreadLocal介绍

    ThreadLocal简介 ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数...

  • ThreadLocal介绍

    我们在说明handler原理和AsyncTask原理的时候都提到了ThreadLocal,当时没有细说,现在来介绍...

  • ThreadLocal介绍

    ThreadLocal,顾名思义,其存储的内容是线程本地的,私有的,我们常用来存储和维护一些资源或者变量,以避免线...

  • ThreadLocal介绍

    ThreadLocal 能做什么 ThreadLocal 是 JDK 提供的一个工具类,它可以为每个使用它的线程创...

  • 介绍ThreadLocal

    1、概览 在本文中,我们将研究下ThreadLocal。ThreadLocal为我们提供了为当前线程单独保存数据的...

  • ThreadLocal介绍

    1. 简单使用 可以看到,同一个线程中设置完userId,在线程中的其他地方可以读取出来使用 2. 实现原理 我们...

网友评论

      本文标题:介绍ThreadLocal

      本文链接:https://www.haomeiwen.com/subject/qaketktx.html