• ThreadLocal内部机制及使用方法


    一、介绍ThreadLocal内部机制之前,先简单说明一下其特点及用途:

    1.ThreadLocal是单线程内共享资源,多线程间无法共享(即线程A访问不了线程B中ThreadLocal存放的值);

    2.ThreadLocal是本地变量,无法跨jvm传递;

    3.ThreadLocal的出现可以减少通过参数来传递(使代码更加简洁,降低耦合性),Hibernate中的OpenSessionInView,就始终保证当前线程只有一个在使用中的Connection(或Hibernate Session),代码如下:

     1 public class ConnectionManager {  
     2   
     3     /** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */  
     4     private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();  
     5       
     6     public static Connection getCurrConnection() {  
     7         // 获取当前线程内共享的Connection  
     8         Connection conn = threadLocal.get();  
     9         try {  
    10             // 判断连接是否可用  
    11             if(conn == null || conn.isClosed()) {  
    12                 // 创建新的Connection赋值给conn(略)  
    13                 // 保存Connection  
    14                 threadLocal.set(conn);  
    15             }  
    16         } catch (SQLException e) {  
    17             // 异常处理  
    18         }  
    19         return conn;  
    20     }  
    21       
    22     /** 
    23      * 关闭当前数据库连接 
    24      */  
    25     public static void close() {  
    26         // 获取当前线程内共享的Connection  
    27         Connection conn = threadLocal.get();  
    28         try {  
    29             // 判断是否已经关闭  
    30             if(conn != null && !conn.isClosed()) {  
    31                 // 关闭资源  
    32                 conn.close();  
    33                 // 移除Connection  
    34                 threadLocal.remove();  
    35                 conn = null;  
    36             }  
    37         } catch (SQLException e) {  
    38             // 异常处理  
    39         }  
    40     }  
    41 }  


    二、ThreadLocal的内部方法

    1.protected T initialValue()

    ①该方法实现只返回 null,并且修饰符为protected,很明显,如果用户想返回初始值不为null,则需要定义线程变量时重写该方法;

    1     /**
    2      * @return the initial value for this thread-local
    3      */
    4     protected T initialValue() {
    5         return null;
    6     }
    1 //定义ThreadLocal时重写initialValue方法,返回用户想要的值
    2 private static ThreadLocal t = new ThreadLocal() {
    3     public Object initialValue() {
    4         A a = new A();
    5         return a;
    6     }
    7 };

    ②返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。

     1     /**
     2      * Returns the value in the current thread's copy of this
     3      * thread-local variable.  If the variable has no value for the
     4      * current thread, it is first initialized to the value returned
     5      * by an invocation of the {@link #initialValue} method.
     6      *
     7      * @return the current thread's value of this thread-local
     8      */
     9     public T get() {
    10         Thread t = Thread.currentThread();
    11         ThreadLocalMap map = getMap(t);
    12         if (map != null) {
    13             ThreadLocalMap.Entry e = map.getEntry(this);
    14             if (e != null)
    15                 return (T)e.value;
    16         }
    17         return setInitialValue();
    18     }    

    2.public T get()
    ①在1中已经提到,该方法返回当前线程变量副本。如果这是线程第一次调用该方法,则创建并初始化此副本。

    3.public void set(T value)

    ①ThreadLocal中有个内部静态类ThreadLocalMap,用来存放当前线程变量副本中的值,键为当前线程变量对象,值为用户设的值;

    ②当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值],这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量(即不同的ThreadLocal,就如1中所说,可以重写initialValue,返回不同类型的子类)。

     1     /**
     2      * Sets the current thread's copy of this thread-local variable
     3      * to the specified value.  Most subclasses will have no need to
     4      * override this method, relying solely on the {@link #initialValue}
     5      * method to set the values of thread-locals.
     6      *
     7      * @param value the value to be stored in the current thread's copy of
     8      *        this thread-local.
     9      */
    10     public void set(T value) {
    11         Thread t = Thread.currentThread();
    12         ThreadLocalMap map = getMap(t);
    13         if (map != null)
    14             map.set(this, value);
    15         else
    16             createMap(t, value);
    17     }

    4.public void remove()

    ①移除此线程中某个ThreadLocal的值,目的是为了减少内存的占用。如果再次访问此线程局部变量,那么在默认情况下它将拥有其initialValue;

    ②只有从jdk1.5开始才有该方法;

    ③当线程结束后,对应该线程的局部变量将自动被垃圾回收,因此显式调用remove清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

     1      /**
     2      * Removes the current thread's value for this thread-local
     3      * variable.  If this thread-local variable is subsequently
     4      * {@linkplain #get read} by the current thread, its value will be
     5      * reinitialized by invoking its {@link #initialValue} method,
     6      * unless its value is {@linkplain #set set} by the current thread
     7      * in the interim.  This may result in multiple invocations of the
     8      * <tt>initialValue</tt> method in the current thread.
     9      *
    10      * @since 1.5
    11      */
    12      public void remove() {
    13          ThreadLocalMap m = getMap(Thread.currentThread());
    14          if (m != null)
    15              m.remove(this);
    16      }

    三、ThreadLocal和同步机制synchonzied相比

    1.synchonzied同步机制是为了实现同步多线程对相同资源的并发访问控制。同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,所以同步操作应该是细粒度的(对象中的不同元素使用不同的锁,而不是整个对象一个锁)。如果同步使用得当,带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。 

    2.ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用ThreadLocal会获得更大的性能。

    3.ThreadLocal中的对象,通常都是比较小的对象。另外使用ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。 

    4.synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。 

    5.Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。 

  • 相关阅读:
    Java 中的定时任务(一)
    超实用 Git 使用方式介绍
    TCP 建立连接为什么要握 3 次手?
    OSI、TCP、IP、UDP 这些都是啥??
    Java 中线程安全问题
    PlantUML——3.Graphviz的安装
    PlantUML——2.中文乱码及解决
    PlantUML——1.Hello
    maven实战系列
    NGUI优化之Drawcall
  • 原文地址:https://www.cnblogs.com/handsomeye/p/5390720.html
Copyright © 2020-2023  润新知