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

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

线程封闭的概念

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

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

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

ThreadLocal解析

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

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

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

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

ThreadLocal类的定义

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

/** * 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 */
public class ThreadLocal<T>

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

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

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<Integer> threadId =
         new ThreadLocal<Integer>() {
             @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();
     }
 }

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

ThreadLocal类中提供的方法

构造方法

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

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

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

成员方法

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

/** * 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 */
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

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

/** * 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);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

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

/** * 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. */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

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

/** * 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 */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

ThreadLocal示例

public class ThreadLocalTest {
    /** threadLocal变量,每个线程都有一个副本,互不干扰 */
    public static ThreadLocal<String> value = new ThreadLocal<>();

    /** * threadlocal测试 * * @throws Exception */
    public void threadLocalTest() throws Exception {

        // threadlocal线程封闭示例
        value.set("这是主线程设置的123"); // 主线程设置值
        String v = value.get();
        System.out.println("线程1执行之前,主线程取到的值:" + v);

        new Thread(new Runnable() {
            @Override
            public void run() {
                String v = value.get();
                System.out.println("线程1取到的值:" + v);
                // 设置 threadLocal
                value.set("这是线程1设置的456");

                v = value.get();
                System.out.println("重新设置之后,线程1取到的值:" + v);
                System.out.println("线程1执行结束");
            }
        }).start();

        Thread.sleep(5000L); // 等待所有线程执行结束

        v = value.get();
        System.out.println("线程1执行之后,主线程取到的值:" + v);

    }

    public static void main(String[] args) throws Exception {
        new ThreadLocalTest().threadLocalTest();
    }
}

相关文章

最新文章

更多