浅谈java中的ThreadLocal类

在谈ThreadLocal之前,先简单说一下一致性问题及其解决方法。

1.一致性问题

发生在多个主体对同一份数据无法达成共识。包括分布式一致性问题、并发问题等。特点是场景多、问题复杂、难以察觉——需要严密的思考甚至数学论证。

2.一致性问题解决方法

一致性问题的解决方法通常有3个。第一,排队:比如,锁、互斥量、管程、屏障等。第二,投票:例如,paxos、Raft等。这两种都会产生额外的开销。还有一种是避免产生一致性问题:比如ThreadLocal等,为解决多线的并发问题提供了新的思路。

3.ThreadLocal是什么

定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)。

特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)

场景:多线程环境下,资源持有、线程一致性、并发计算、线程安全等场景。

4.ThreaLocal的API

构造函数ThreadLocal<T>()

 initialValue() :用于初始化

void set(T value):设置当前线程的线程局部变量的值。 

T get():获得当前线程所对应的线程局部变量的值

void remove():删除当前线程中线程局部变量的值

ThreadLocal能使线程中的某个值与保存值的对象关联起来。它提供了get和set等访问方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。

5.ThreadLocal的简单实用

ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。例如,在单线程应用中可能会维持一个全局的数据库连接,并在程序启动时初始化这个连接对象,从而避免在调用没个方法时都要传递一个Connection对象。由于JDBC的连接对象不一定是线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的。通过将JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的连接。

private static ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>(){
        @Override
        protected Connection initialValue(){
            return DriverManager.getConnection(DB_UR);
        }
    };

    public static Connection getConnection(){
        return connectionThreadLocal.get();
    }
public class Test {

    static ThreadLocal<Long> v =  new ThreadLocal<Long>(){
        @Override
        protected Long initialValue(){//用于返回该线程局部变量的初始值
            return Thread.currentThread().getId();
        }
    };

    public static void main(String[] args){

        for (int i = 0; i < 100; i++) {
           new Thread(()->{
               System.out.println(v.get());//返回当前线程对应的线程的局部变量
           }).start();
        }
    }
}

当某个线程初次调用ThreadLocal的get()方法时,就会调用initialValue()来获取初始值。从概念上看,可以将ThreadLocal<T>理解为包含了Map<Thread,T>对象,其中保存来了特定于该线程的值,但ThreadLocal的实现并非如此。这些特定了线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收。

5.ThreadLocal的原理

ThreadLocal的类图结构:

从类图中我们可以看出,ThreadLocal中有一个内部类ThreadLocalMap,实际上它类似于一个HashMap,用于存放每个线程的变量副本, Map中元素的键为线程对象,而值对应线程的变量副本。

注意:

1.ThreadLocal使用不当,可能会导致内存泄漏问题,隐藏使用完毕后,最好使用ThreadLocal.remove()方法将这个变量移除。

2.ThreadLocal 无法解决共享对象的更新问题, ThreadLocal 对象建议使用 static修饰。这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象 ( 只要是这个线程内定义的 ) 都可以操控这个变量。

猜你喜欢

转载自blog.csdn.net/lovebaby1689/article/details/104200550