【java高性能编程基础】 - 线程封闭与ThreadLocal源码分析

x33g5p2x  于2022-01-13 转载在 Java  
字(5.9k)|赞(0)|评价(0)|浏览(491)

线程封闭的概念

在多线程访问共享可变数据时,就会涉及到线程间数据同步的问题。如果数据都被封闭在各自的线程之中,就不需要同步了,也不会出现线程安全的问题了。这种通过将数据封闭在线程中而避免使用同步的技术就叫线程封闭

实现线程封闭的方法有多种,比如

  • ad-hoc线程封闭
  • 局部变量
  • ThreadLocal

ThreadLocal解析

ThreadLocal是jdk提供的一个实现线程封闭的api,它位于java.lang包下。

ThreadLocal看名字会容易理解为“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量。

ThreadLocal是Java里一种特殊的变量,是一个线程级别变量,每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了,在并发模式下是绝对安全的变量。

打开Thread源码我们可以看到,每个Thread对象内部都维护了一个ThreadLocalMap,这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。

ThreadLocal类的定义

接着,再来到ThreadLocal类中查看源码中它的类声明:

  1. /** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread that accesses one (via its * {@code get} or {@code set} method) has its own, independently initialized * copy of the variable. {@code ThreadLocal} instances are typically private * static fields in classes that wish to associate state with a thread (e.g., * a user ID or Transaction ID). * * <p>For example, the class below generates unique identifiers local to each * thread. * A thread's id is assigned the first time it invokes {@code ThreadId.get()} * and remains unchanged on subsequent calls. * <pre> * import java.util.concurrent.atomic.AtomicInteger; * * public class ThreadId { * // Atomic integer containing the next thread ID to be assigned * private static final AtomicInteger nextId = new AtomicInteger(0); * * // Thread local variable containing each thread's ID * private static final ThreadLocal&lt;Integer&gt; threadId = * new ThreadLocal&lt;Integer&gt;() { * &#64;Override protected Integer initialValue() { * return nextId.getAndIncrement(); * } * }; * * // Returns the current thread's unique ID, assigning it if necessary * public static int get() { * return threadId.get(); * } * } * </pre> * <p>Each thread holds an implicit reference to its copy of a thread-local * variable as long as the thread is alive and the {@code ThreadLocal} * instance is accessible; after a thread goes away, all of its copies of * thread-local instances are subject to garbage collection (unless other * references to these copies exist). * * @author Josh Bloch and Doug Lea * @since 1.2 */
  2. public class ThreadLocal<T>

这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为每个线程(通过其get或set方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。

例如,下面的类生成每个线程本地的唯一标识符。 线程的ID在第一次调用ThreadId.get()时被分配,并在后续调用中保持不变。

  1. import java.util.concurrent.atomic.AtomicInteger;
  2. public class ThreadId {
  3. // Atomic integer containing the next thread ID to be assigned
  4. private static final AtomicInteger nextId = new AtomicInteger(0);
  5. // Thread local variable containing each thread's ID
  6. private static final ThreadLocal<Integer> threadId =
  7. new ThreadLocal<Integer>() {
  8. @Override protected Integer initialValue() {
  9. return nextId.getAndIncrement();
  10. }
  11. };
  12. // Returns the current thread's unique ID, assigning it if necessary
  13. public static int get() {
  14. return threadId.get();
  15. }
  16. }

只要线程存活并且ThreadLocal实例可以访问,每个线程都保存对其线程局部变量副本的隐式引用; 线程消失后,线程本地实例的所有副本都将被垃圾收集(除非存在对这些副本的其他引用)。

ThreadLocal类中提供的方法

构造方法

  1. /** * Creates a thread local variable. * @see #withInitial(java.util.function.Supplier) */
  2. public ThreadLocal() {
  3. }

ThreadLocal中只提供了一个空参构造方法,用法如下:

  1. ThreadLocal<T> var = new ThreadLocal<T>();

成员方法

1. withInitial(Supplier<? extends S> supplier)
创建线程局部变量

  1. /** * Creates a thread local variable. The initial value of the variable is * determined by invoking the {@code get} method on the {@code Supplier}. * * @param <S> the type of the thread local's value * @param supplier the supplier to be used to determine the initial value * @return a new thread local variable * @throws NullPointerException if the specified supplier is null * @since 1.8 */
  2. public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
  3. return new SuppliedThreadLocal<>(supplier);
  4. }

2. get()
返回当前线程的线程局部变量的副本中的值。 如果变量没有当前线程局部变量的值,则首先将其初始化为调用initialValue()方法返回的值。

  1. /** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */
  2. public T get() {
  3. Thread t = Thread.currentThread();
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null) {
  6. ThreadLocalMap.Entry e = map.getEntry(this);
  7. if (e != null) {
  8. @SuppressWarnings("unchecked")
  9. T result = (T)e.value;
  10. return result;
  11. }
  12. }
  13. return setInitialValue();
  14. }

3. set(T value)
将当前线程的线程局部变量的副本设置为指定的值。 大多数子类将无需重写此方法,仅依靠initialValue()方法设置线程本地值的值。

  1. /** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */
  2. public void set(T value) {
  3. Thread t = Thread.currentThread();
  4. ThreadLocalMap map = getMap(t);
  5. if (map != null)
  6. map.set(this, value);
  7. else
  8. createMap(t, value);
  9. }

4. remove()
删除此线程局部变量的当前线程的值。 如果此线程本地变量随后是当前线程的read ,则其值将通过调用其initialValue()方法重新初始化。 这可能导致当前线程中的initialValue方法的多次调用。

  1. /** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */
  2. public void remove() {
  3. ThreadLocalMap m = getMap(Thread.currentThread());
  4. if (m != null)
  5. m.remove(this);
  6. }

ThreadLocal示例

  1. public class ThreadLocalTest {
  2. /** threadLocal变量,每个线程都有一个副本,互不干扰 */
  3. public static ThreadLocal<String> value = new ThreadLocal<>();
  4. /** * threadlocal测试 * * @throws Exception */
  5. public void threadLocalTest() throws Exception {
  6. // threadlocal线程封闭示例
  7. value.set("这是主线程设置的123"); // 主线程设置值
  8. String v = value.get();
  9. System.out.println("线程1执行之前,主线程取到的值:" + v);
  10. new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. String v = value.get();
  14. System.out.println("线程1取到的值:" + v);
  15. // 设置 threadLocal
  16. value.set("这是线程1设置的456");
  17. v = value.get();
  18. System.out.println("重新设置之后,线程1取到的值:" + v);
  19. System.out.println("线程1执行结束");
  20. }
  21. }).start();
  22. Thread.sleep(5000L); // 等待所有线程执行结束
  23. v = value.get();
  24. System.out.println("线程1执行之后,主线程取到的值:" + v);
  25. }
  26. public static void main(String[] args) throws Exception {
  27. new ThreadLocalTest().threadLocalTest();
  28. }
  29. }

相关文章

最新文章

更多