线程本地变量ThreadLocal

本文详细介绍了ThreadLocal类在Java中的作用及其实现原理。通过具体示例展示了如何利用ThreadLocal为每个线程提供独立的变量副本,避免了多线程间的变量访问冲突。比较了ThreadLocal与传统同步机制在解决多线程资源共享问题上的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PS

关于多线程共享变量ThreadLocal<T>

变量值的共享可以使用public static 变量的形式,所有的线程都可以使用同一个public static变量。如果想实现每一个线程都有自己的共享变量,如何实现?

JDK中提供了类ThreadLocal<T>可以解决这样的问题。

原理:
ThreadLocal所属包java.lang.ThreadLocal<T> 是个泛型class
ThreadLocal为解决多线程并发问题提供了新的思路,使用这个类库可以简洁的实现多线程程序,
当使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供独立的变量副本,所以没一个线程都可以改变自己的副本,而不会影响到其他线程对于的变量副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

ThreadLocal为每一个线程维护变量的副本的原理:在ThreadLocal类中有一个Map集合,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本,查看ThreadLocal的源码可以看到
其开放的方法主要有:
public T get()  返回当前线程所对应的线程局部变量。
public void set(T value)  设置当前线程的线程局部变量的值。
public void remove() 将当前线程局部变量的值删除,目的是为了减少内存的占用,注(当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度)

public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) 初始化值


在创建变量设置值的时候set(T value) ,可以看到使用到了ThreadLocalMap类,该类是ThreadLocal中的一个静态内部类

  /**
     * 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);
    }

  /**
     * 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);
    }

ThreadLocalMap用于存储每一个线程的变量副本:

/**
     * ThreadLocalMap is a customized hash map suitable only for
     * maintaining thread local values. No operations are exported
     * outside of the ThreadLocal class. The class is package private to
     * allow declaration of fields in class Thread.  To help deal with
     * very large and long-lived usages, the hash table entries use
     * WeakReferences for keys. However, since reference queues are not
     * used, stale entries are guaranteed to be removed only when
     * the table starts running out of space.
     */
    static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

...省略....
}


示例:

1、创建两个线程实现变量存储

public class ThreadA extends Thread {

	public void run() {
		// 给当前线程设置变量值
		ThreadLocalTool.THREAD_LOCAL.set("threadA:[" + Math.random() + "]");
		// 输出
		System.out.println("get threadA:" + ThreadLocalTool.THREAD_LOCAL.get());
	}
}

public class ThreadB extends Thread {

	public void run() {
		// 给当前线程设置变量值
		ThreadLocalTool.THREAD_LOCAL.set("threadB:[" + Math.random() + "]");
		// 输出
		System.out.println("get threadB:" + ThreadLocalTool.THREAD_LOCAL.get());
	}
}


2、运行测试Client

public class Client {

	public static void main(String[] args) {
		ThreadA tA = new ThreadA();
		ThreadB tB = new ThreadB();
		// 给当前线程设置变量值
		ThreadLocalTool.THREAD_LOCAL.set("main:[" + Math.random() + "]");

		tA.start();
		tB.start();

		System.out.println("get main:" + ThreadLocalTool.THREAD_LOCAL.get());
	}
}

以上示例运行之后,输出:

get main:main:[0.7622350866507818]
get threadB:threadB:[0.6860560404190107]
get threadA:threadA:[0.22195491171797677]

可以看出Threadlocal类解决的是变量在不同线程间的隔离性,也就是不同线程用自己的值,不同线程的值是可以放入Threadlocal中进行保存的。


Threadlocal的优势

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。
1)在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
2)而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
3)概括起来说,对于多线程资源共享的问题,同步机制采用了"以时间换空间"的方式,而ThreadLocal采用了"以空间换时间"的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值