zoukankan      html  css  js  c++  java
  • ThreadLocal的意义和实现

    可以想像,如果一个对象的可变的变量被多个线程访问时,必然是不安全的。

      在单线程应用可能会维持一个全局的数据库连接,并在程序启动时初始化这个连接对象,从而避免在调用每个方法时都传递一个Connection对象。ThreadUnsafe类就是这样做的:

    public class ThreadUnsafe {
         private static Connection connection = DriverManager.getConnection(DB_URL);
        
         public void Connection getConnection{  /* 在多线程应用中,connection 在被多个线程访问 */
            return connection;
        }
    }

      但是JDBC连接对象不一定是线程安全的,在多个线程访问到Connection时,就可能出现安全问题。为了解决这个问题,ThreadLocal类提供了安全的做法。

      通过将JDBC的Connection对象封装在ThreadLocal对象中,当每个线程访问需要Connection对象时,ThreadLocal对象返回的是一个副本。

    public class ThreadUnsafe {
         private static ThreadLocal<Connection> connectionHodler = new ThreadLocal<>{
             public Connection initialValue() {
                 return DriverManager.getConnection(DB_URL);
             }
         } 
        
         public void Connection getConnection{ /* 即使多个线程可以访问,依然安全 */
            return connectionHolder.get();
        }
    }

    ThreadLocal是如何实现这种功能? 

      首先,在Thread类中有一个threadLocals的实例变量,这是一个Map,保存了与线程相关的ThreadLocal对象封装的变量。

     /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;

      当线程初次调用ThreadLocal对象的get方法时,就会调用initialValue()来获取初始值。

        /**
         * 返回ThreadLocal封装的对象。*/
        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) { /* 首次调用map为null */
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue(); /* 首次调用的返回值 */
        }
    
        /**
         * 初始化封装在ThreadLocal中对象的值。*/
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value); //为什么键值是ThreadLocal对象?,因为一个线程对象可能有使用多个ThreadLocal封闭的变量
            else
                createMap(t, value);
            return value;
        }
    
        /**
         * 更新封装在ThreadLocal中对象的值*/
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
         public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
      
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
        /**
         * 创建一个Map,用于保存ThreadLocal和其封装的对象。*/
        void createMap(Thread t, T firstValue) { 
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }

      注意:ThreadLocalMap在ThreadLocal类中声明,却是在Thread类中使用的,原因在于,当线程结束时,这些特定于线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收。

      ThreadLocal类实现的是一种线程封闭技术。将变量封闭在单线程中,从而避免同步。

    参考: 《Java Concurrency in Practice》 P35&P37 

  • 相关阅读:
    TCP粘包的拆包处理
    字节序列化
    同步,异步,阻塞和非阻塞
    用Doxygen生成文档
    Visual Studio新建的源文件的默认编码
    Boost编程之获取可执行文件的当前路径
    特征点寻找的基础数据结构和函数
    寻找Harris、Shi-Tomasi和亚像素角点
    特征点的基本概念和如何找到它们
    工业相机与普通相机的差别
  • 原文地址:https://www.cnblogs.com/yvkm/p/10664109.html
Copyright © 2011-2022 走看看