ThreadLocal的使用场景及实现原理

Posted Acode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal的使用场景及实现原理相关的知识,希望对你有一定的参考价值。

1. 什么是ThreadLocal?

线程局部变量(通常,ThreadLocal变量是private static修饰的,此时ThreadLocal变量相当于成为了线程内部的全局变量)

2. 使用场景

  • 单例的对象中属性线程内共享,线程间无关;
  • 工具类属性线程内共享,线程间无关。

为什么这么说呢?下面看4个问题:

(1)对象为什么要是单例的?

如果对象不是单例的,那么大可以每次都new一个对象,然后对用到属性赋值就行,代码如下:

public class Service {

    private String key;

    void A() {
        // 代码实现,中间用到key
    }

    void B() {
        // 代码实现,中间用到key
    }
    
    // 省略get和set方法

}

在使用时,每个线程都new Service(),并对key赋值,然后调用其中的方法就行了,保证方法A和B用的key都是一个值。

(2)单例对象的属性共享

如果希望单例对象中的某个属性可以被共享,那么将属性声明为static就行了:

public class Service {

    private static String key;

    // 省略其他方法

}

上面的实现确实保证了所有方法都能使用key,然而,在多线程环境下,key是不安全的。

(3)单例对象在线程内属性共享,不同线程间相互不影响

这就轮到ThreadLocal上场了:

public class Service {

    private static ThreadLocal<String> key = new ThreadLocal<String>() {
        protected String initialValue() {
            return Thread.currentThread().getName();
        }
    };

    public void A() {
        System.out.println("methodA: " + key.get());
        key.set("methodA: " + key.get());
    }

    public void B() {
        System.out.println("methodB: " + key.get());
    }
}

使用方式:

public class ThreadLocalTest {

    public static void main(String[] args) {

        final Service service = new Service(); //模拟单例对象的使用

        new Thread(new Runnable() {
            @Override
            public void run() {
                service.A();
                service.B();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                service.A();
                service.B();
            }
        }).start();

    }

}

运行结果:

methodA: Thread-0
methodB: methodA: Thread-0
methodA: Thread-1
methodB: methodA: Thread-1

(4)工具类中线程共享,线程间无关

工具类的代码:

public final class XUtil {

    private static ThreadLocal<String> key = new ThreadLocal<String>();

    private XUtil() {
    }

    public static void A() {
        // 实现
    }

    public static void B() {
        // 实现
    }

}

在使用XUtil时,每个线程中key可以使用,不同线程间不受影响。

3. ThreadLocal的实现原理

为什么一个static的变量(即:类变量)可以做到:线程内共享,线程间无关?

看下get方法吧:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map!=null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if(e!=null)
            return (T)e.value;
    }
    return setInitialValue();
}

关键就在getMap()方法:

ThreadLocalMap getMap(Thread t) {
    return t.threadlocals;
}

查看Thread类:

ThreadLocal.ThreadLocalMap threadLocals = null;

原来数据就存在当前线程内部,自然就能做到线程内共享,线程间无关了。

以上是关于ThreadLocal的使用场景及实现原理的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal的使用及原理分析

java ThreadLocal(应用场景及使用方式及原理)

javaThreadLocal线程变量的实现原理和使用场景

ThreadLocal原理及使用示例

ThreadLocal的原理及用法

ThreadLocal 应用及其原理详解