ThreadLocal使用及原理

      当访问共享的可变数据时,为了线程安全同常需要进行同步,一般避免使用同步的方式就是不共享数据。如果共享的变量只能在单个线程内被访问,那么就不需要进行同步了。这种技术称为线程封闭。

ThreadLocal提供了一种线程封闭的实现。ThreadLocal提供了get和set方法,每个线程调用get方法时只会返回该线程上次调用set方法设置的值(默认没用set值时返回null)。这样就可以避免共享变量在不同的线程之间共享。如下示例:

public class ThreadLocalCase {
    private static final ThreadLocal<Integer> LOCAL = new ThreadLocal<>();
    private static Integer integer = 0;
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                LOCAL.set(100);
                integer=100;
                System.out.println(Thread.currentThread().getName()+" local value:"+LOCAL.get());
                System.out.println(Thread.currentThread().getName()+" int value:"+integer);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" local value:"+LOCAL.get());
                System.out.println(Thread.currentThread().getName()+" int value:"+integer);
            }
        }).start();
    }
}
//控制台输出
Thread-0 local value:100
Thread-0 int value:100
Thread-1 local value:null
Thread-1 int value:100

       这个例子中ThreadLocalCase类持有一个ThreadLocal类型的类变量和一个Integer类型的类变量。当线程0和对integer操作时,线程0设置的值在线程1中可见,显然integer对象不是线程安全的。而同样线程0对于ThreadLocal对象中的integer进行操作时,线程0设置的值在线程1中不可见。说明实现了线程隔离。

ThreadLocal是如何实现线程封闭的呢?

        对于每一个运行的线程都会有一个对应的线程类Thread实例,在代码中我们可以通过Thread.currentThread()获取这个线程对象。Thread类中持有一个ThreadLocal.ThreadLocalMap类型的属性,这个属性的访问权限是package级别的,java.lang包以外的类无法操作这个变量。而ThreadLocal类和Thread类在同一个包下,可以通过Thread实例操作这个属性。

public
class Thread implements Runnable {
    ......
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    .....
    }

        当调用ThreadLocal的set方法时,会先获取当前线程实例,然后拿到Thread实例的ThreadLocal.ThreadLocalMap类型的引用,将当前ThreadLocal对象引用作为key,将要保存的值作为value保存到map中。这样就完成赋值操作。

public void set(T value) {
    //获取当前线程实例
    Thread t = Thread.currentThread();
    //获取当前线程实例中的ThreadLocal.ThreadLocalMap类型的引用
    ThreadLocalMap map = getMap(t);
    if (map != null)
    //将当前ThreadLocal对象引用作为key,将要保存的值作为value保存到map中
        map.set(this, value);
    else
    //第一次调用时map为空,进行初始化
        createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

        当调用ThreadLocal的get方法时,同样的,会先获取当前线程实例,然后拿到Thread实例的ThreadLocal.ThreadLocalMap类型的引用,将当前ThreadLocal对象引用作为key,从map中获取对应的值。

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;
        }
    }
    //若map为空,返回null
    return setInitialValue();
}

        上面例子中,我们在Thread-0线程中,set一个值,这个值最终保存在Thread-0线程所对应的线程实例的threadLocalMap中。所以在Thread-1线程调用get方法时,Thread-1线程所对应的线程实例的threadLocalMap仍然为空,所以会返回null。

 

 

ThreadLocalJava中的一个线程级别的变量,它提供了一种简单的方式来在多线程环境中维护变量的值。每个线程都拥有自己独立的ThreadLocal实例,并且可以通过该实例来获取和设置其对应的变量的值。 ThreadLocal原理是通过在每个线程中创建一个独立的副本来存储变量的值。这样,每个线程都可以独立地访问和修改自己的副本,而不会对其他线程产生影响。 ThreadLocal使用非常简单。首先,我们需要创建一个ThreadLocal对象,并指定要存储的变量类型。然后,我们可以通过调用ThreadLocal的get方法来获取当前线程中与该ThreadLocal对象关联的变量值,如果当前线程还没有设置过该变量,get方法会返回null。类似地,我们可以通过调用ThreadLocal的set方法来设置当前线程中与该ThreadLocal对象关联的变量值。 下面是一个简单的示例代码: ``` public class ThreadLocalExample { private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>(); public static void main(String[] args) { THREAD_LOCAL.set("Hello, world!"); Thread thread1 = new Thread(() -> { THREAD_LOCAL.set("Hello from thread 1!"); System.out.println(THREAD_LOCAL.get()); }); Thread thread2 = new Thread(() -> { THREAD_LOCAL.set("Hello from thread 2!"); System.out.println(THREAD_LOCAL.get()); }); thread1.start(); thread2.start(); System.out.println(THREAD_LOCAL.get()); } } ``` 在上面的示例中,我们创建了一个ThreadLocal对象`THREAD_LOCAL`用于存储String类型的变量。首先我们通过调用`THREAD_LOCAL.set("Hello, world!")`方法在主线程中设置了变量的值。然后,我们创建了两个新的线程并分别在其中设置了不同的值,并打印出来。最后,在主线程中我们也打印了变量的值。 运行上面的代码,你会看到输出结果类似于: ``` Hello from thread 1! Hello from thread 2! Hello, world! ``` 可以看到,每个线程都可以独立地访问和修改自己的变量副本,不会对其他线程产生影响。这样就确保了在多线程环境中,每个线程都可以维护自己的变量状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值