用于并发Java编程的ThreadLocal的详细说明和示例

电脑杂谈  发布时间:2020-05-23 19:03:50  来源:网络整理

threadlocal应用实例_xpath实例使用详解_threadlocal使用实例

Java理解ThreadLocal

摘要:

ThreadLocal,也称为线程局部变量,是Java中的一种特殊的线程绑定机制. 它用于确保不同线程之间的变量隔离,以便每个线程可以处理自己的状态. 此外,本文以ThreadLocal类的源代码为切入点,深入分析了ThreadLocal类的工作原理,并给出了应用场景和一般使用步骤.

一个. 了解ThreadLocal

1). ThreadLocal概述

ThreadLocal,也称为线程局部变量,是Java中的一种特殊的线程绑定机制. 它可以为使用该变量的每个线程提供变量值的副本,并且每个线程可以独立更改自身. ,不与其他线程的副本冲突. 通过ThreadLocal访问的数据始终与当前线程相关,也就是说,JVM为每个正在运行的线程绑定了一个私有的本地实例访问空间,从而为多线程环境中经常发生的并发访问问题提供了一种方法. 一种隔离机制.

2). JDK中的ThreadLocal定义

ThreadLocal

此类提供线程局部变量. 这些变量与普通变量(副本)的不同之处在于,每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本. ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或交易ID).

只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本(参见下图)隐式引用. 线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用).

threadlocal应用实例_xpath实例使用详解_threadlocal使用实例

我们可以从中提取三个要点:

每个线程都有一个有关ThreadLocal变量的私有值

每个线程都有一个独立于其他线程的上下文来保存此变量的值,并且其他线程不可见.

与变量的初始值无关

可以为ThreadLocal赋予一个初始值,以便每个线程都可以获取该初始值的副本,并且其他线程看不到每个线程对该值的修改.

状态与某个线程相关联

ThreadLocal不是用来解决共享变量的问题,也不是用来协调线程同步的,而是引入了一种机制来促进每个线程处理自己的状态. 理解这一点对于正确使用ThreadLocal至关重要.

3). 应用场景

ThreadLocal类的主要解决方案是将每个线程与其自己的值绑定,以方便其对自己状态的处理. 从视觉上讲,可以将ThreadLocal变量与用于全局存储数据的框进行比较,并且可以将每个线程的私有数据存储在框中. 例如,以下类用于生成每个线程唯一的本地标识符. 线程ID是在首次调用uniqueNum.get()时分配的,在以后的调用中不会更改.

xpath实例使用详解_threadlocal使用实例_threadlocal应用实例

import java.util.concurrent.atomic.AtomicInteger;
public class UniqueThreadIdGenerator {
  private static final AtomicInteger uniqueId = new AtomicInteger(0);
  private static final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
    @Override
    protected Integer initialValue() {
      return uniqueId.getAndIncrement();
    }
  };
  public static void main(String[] args) {
    Thread[] threads = new Thread[5];
    for (int i = 0; i < 5; i++) {
      String name = "Thread-" + i;
      threads[i] = new Thread(name){
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + ": "
              + uniqueNum.get());
        }
      };
      threads[i].start();
    }
    System.out.println(Thread.currentThread().getName() + ": "
        + uniqueNum.get());
  }
}/* Output(输出结果不唯一): 
    Thread-1: 2
    Thread-0: 0
    Thread-2: 3
    main: 1
    Thread-3: 4
    Thread-4: 5
 *///:~

两个. ThreadLocal类的深入分析

下面threadlocal使用实例,让我们看一下ThreadLocal的特定实现. 此类提供了四种方法:

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

其中,get()方法用于获取保存在当前线程中的ThreadLocal变量的值,set()用于设置当前线程中的ThreadLocal变量的值,而remove()是用于删除当前线程中的相关ThreadLocal变量initialValue()是一种受保护的方法,通常需要重写.

1. 原理研究

1). 入口点: get()

首先,让我们先看一下源代码:

 /**
   * 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
   */
  public T get() {
    Thread t = Thread.currentThread();  // 获取当前线程对象
    ThreadLocalMap map = getMap(t);   // 获取当前线程的成员变量 threadLocals
    if (map != null) {
      // 从当前线程的 ThreadLocalMap 获取该 thread-local variable 对应的 entry
      ThreadLocalMap.Entry e = map.getEntry(this);  
      if (e != null)   
        return (T)e.value;  // 取得目标值
    }
    return setInitialValue(); 
  }

2). 关键点: setInitialValue()

threadlocal应用实例_threadlocal使用实例_xpath实例使用详解

/**
   * Variant of set() to establish initialValue. Used instead
   * of set() in case user has overridden the set() method.
   *
   * @return the initial value
   */
  private T setInitialValue() {
    T value = initialValue();   // 默认实现返回 null
    Thread t = Thread.currentThread();  // 获得当前线程
    ThreadLocalMap map = getMap(t);   // 得到当前线程 ThreadLocalMap类型域 threadLocals
    if (map != null)
      map.set(this, value); // 该 map 的键是当前 ThreadLocal 对象
    else
      createMap(t, value);  
    return value;
  }

接下来,我们看一下上述方法中涉及的三种方法: initialValue(),set(this,value)和createMap(t,value).

(1)initialValue()

 /**
   * Returns the current thread's "initial value" for this
   * thread-local variable. This method will be invoked the first
   * time a thread accesses the variable with the {@link #get}
   * method, unless the thread previously invoked the {@link #set}
   * method, in which case the <tt>initialValue</tt> method will not
   * be invoked for the thread. Normally, this method is invoked at
   * most once per thread, but it may be invoked again in case of
   * subsequent invocations of {@link #remove} followed by {@link #get}.
   *
   * <p>This implementation simply returns <tt>null</tt>; if the
   * programmer desires thread-local variables to have an initial
   * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
   * subclassed, and this method overridden. Typically, an
   * anonymous inner class will be used.
   *
   * @return the initial value for this thread-local
   */
  protected T initialValue() {
    return null;      // 默认实现返回 null
  }

(2)createMap()

/**
   * Create the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   *
   * @param t the current thread
   * @param firstValue value for the initial entry of the map
   * @param map the map to store.
   */
  void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue); // this 指代当前 ThreadLocal 变量,为 map 的键 
  }


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-219713-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    • 李海玉
      李海玉

      听得我分分钟不想看了

    热点图片
    拼命载入中...