Java并发编程的艺术笔记(四)——ThreadLocal的使用

ThreadLocal,即线程变量,是一个以****ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

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 12 //获取当前线程 13 Thread t = Thread.currentThread(); 14 //得到线程的ThredLocalMap 15 ThreadLocalMap map = getMap(t); 16 //如果map不为空,则将当前线程的对象作为key,传进来的参数作为value存储 17 if (map != null) 18 map.set(this, value); 19 else 20 createMap(t, value); 21 }

看一下ThredLocalMap是什么:

1static class ThreadLocalMap { 2 3 /** 4 * The entries in this hash map extend WeakReference, using 5 * its main ref field as the key (which is always a 6 * ThreadLocal object). Note that null keys (i.e. entry.get() 7 * == null) mean that the key is no longer referenced, so the 8 * entry can be expunged from table. Such entries are referred to 9 * as "stale entries" in the code that follows. 10 */ 11 static class Entry extends WeakReference<ThreadLocal<?>> { 12 /** The value associated with this ThreadLocal. */ 13 Object value; 14 15 Entry(ThreadLocal<?> k, Object v) { 16 super(k); 17 value = v; 18 } 19 } 20 21 .......

看到这是ThreadLocal的一个内部类,使用Entry类进行存储。K是我们的ThredLocal对象。

总结:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象

再来看下get方法:

1public T get() { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) { 7 @SuppressWarnings("unchecked") 8 T result = (T)e.value; 9 return result; 10 } 11 } 12 return setInitialValue(); 13 }

拿到这个entry的value。

ThreadLocal本身并不存值,它只是作为ThreadLocalMap的key,来获取value,因此能实现数据隔离。

注意:由于ThreadLocalMap的生命周期和Thread一样长,因此要手动remove掉对应的key,不然会造成内存泄露。

使用场景:

1.管理Connection,尤其是管理数据库连接。

频繁创建和关闭connection是一件很耗时的操作,因此要用到数据库连接池。ThreadLocal可以很好的管理数据库连接,因为它能够实现当前线程的操作都是用同一个Connection,保证了事务!

1public class ConnectionUtil { 2 private static Logger logger = LoggerFactory.getLogger(ConnectionUtil.class); 3 //数据库连接池 4 private static BasicDataSource dataSource; 5 //为不同的线程管理连接 6 private static ThreadLocal<Connection> local; 7 8 static { 9 BufferedReader br = null; 10 Properties ipp_prop = new Properties(); 11 12 try { 13 String propertiesurl = System.getProperty("user.dir") + "/ipp_parser.properties"; 14 br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(propertiesurl)), "utf-8")); 15 ipp_prop.load(br); 16 br.close(); 17 } catch (Exception e1) { 18 e1.printStackTrace(); 19 } 20 21 dataSource = new BasicDataSource(); 22 dataSource.setDriverClassName(ipp_prop.getProperty("db.driver")); 23 dataSource.setUrl(ipp_prop.getProperty("db.url")); 24 dataSource.setUsername(ipp_prop.getProperty("db.user")); 25 dataSource.setPassword(ipp_prop.getProperty("db.password")); 26 //初始连接 27 dataSource.setInitialSize(Integer.parseInt(ipp_prop.getProperty("db.initsize"))); 28 //最大连接 29 dataSource.setMaxTotal(Integer.parseInt(ipp_prop.getProperty("db.maxtotal"))); 30 //最长等待时间 31 dataSource.setMaxWaitMillis(Integer.parseInt(ipp_prop.getProperty("db.maxwait"))); 32 //最小空闲 33 dataSource.setMinIdle(Integer.parseInt(ipp_prop.getProperty("db.minidle"))); 34 dataSource.setMaxIdle(Integer.parseInt(ipp_prop.getProperty("db.maxidle"))); 35 //初始化线程池本地 36 local = new ThreadLocal<>();/**得到连接 37 * @return 38 * @throws SQLException 39 */ 40 public static Connection getOracleConnection() throws SQLException { 41 //获取Connection对象 42 Connection connection = dataSource.getConnection(); 43 //把Connection放进local里 44 local.set(connection); 45 logger.info("get oracleConnection"); 46 return connection; 47 } 48 49 public static void closeOracleConnection(){ 50 Connection connection = local.get(); 51 52 try { 53 if (connection != null) { 54 //设置自动提交 55 connection.setAutoCommit(true); 56 //连接还给连接池 57 connection.close(); 58 local.remove(); 59 logger.info("close oracleConnection"); 60 } 61 } catch (SQLException e) { 62 e.printStackTrace(); 63 } 64 } 65}
点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java ThreadLocal

ThreadLocal是什么定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)场景:多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)ThreadLocal基本API 构

ThreadLocal源码解析及实战应用

ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。ThreadLocal在设计之初就是为解决并发问题而提供一种方案,每个线程维护一份自己的数据,达到线程隔离的效果。

ThreadLocal源码分析

最近在学多线程并发的知识,发现好像ThreadLoca还挺重要,决定看看源码以及查找各方资料来学习一下。ThreadLocal能够提供线程的局部变量,让每个线程都可以通过set/get来对这个局部变量进行操作,不会和其它线程的局部变量进行冲突,实现了线程的数据隔离。首先是ThreadLocal的结构:每个Thread维护一个ThreadLocalMap,这个

Java ThreadLocal的内存泄漏问题

ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现。常见的ThreadLocal用法有:\存储单个线程上下文信息。比如存储id等;\使变量线程安全。变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;\减少参数传递。比如做一个trace工具,能够输出工程从开始到结