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

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

/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) { //获取当前线程
Thread t = Thread.currentThread();
//得到线程的ThredLocalMap
ThreadLocalMap map = getMap(t);
//如果map不为空,则将当前线程的对象作为key,传进来的参数作为value存储
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

看一下ThredLocalMap是什么:

static class ThreadLocalMap {

        /**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
} .......

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

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

再来看下get方法:

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;
}
}
return setInitialValue();
}

拿到这个entry的value。

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

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

使用场景:

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

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

public class ConnectionUtil {
private static Logger logger = LoggerFactory.getLogger(ConnectionUtil.class);
//数据库连接池
private static BasicDataSource dataSource;
//为不同的线程管理连接
private static ThreadLocal<Connection> local; static {
BufferedReader br = null;
Properties ipp_prop = new Properties(); try {
String propertiesurl = System.getProperty("user.dir") + "/ipp_parser.properties";
br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(propertiesurl)), "utf-8"));
ipp_prop.load(br);
br.close();
} catch (Exception e1) {
e1.printStackTrace();
} dataSource = new BasicDataSource();
dataSource.setDriverClassName(ipp_prop.getProperty("db.driver"));
dataSource.setUrl(ipp_prop.getProperty("db.url"));
dataSource.setUsername(ipp_prop.getProperty("db.user"));
dataSource.setPassword(ipp_prop.getProperty("db.password"));
//初始连接
dataSource.setInitialSize(Integer.parseInt(ipp_prop.getProperty("db.initsize")));
//最大连接
dataSource.setMaxTotal(Integer.parseInt(ipp_prop.getProperty("db.maxtotal")));
//最长等待时间
dataSource.setMaxWaitMillis(Integer.parseInt(ipp_prop.getProperty("db.maxwait")));
//最小空闲
dataSource.setMinIdle(Integer.parseInt(ipp_prop.getProperty("db.minidle")));
dataSource.setMaxIdle(Integer.parseInt(ipp_prop.getProperty("db.maxidle")));
//初始化线程池本地
local = new ThreadLocal<>();/**得到连接
* @return
* @throws SQLException
*/
public static Connection getOracleConnection() throws SQLException {
//获取Connection对象
Connection connection = dataSource.getConnection();
//把Connection放进local里
local.set(connection);
logger.info("get oracleConnection");
return connection;
} public static void closeOracleConnection(){
Connection connection = local.get(); try {
if (connection != null) {
//设置自动提交
connection.setAutoCommit(true);
//连接还给连接池
connection.close();
local.remove();
logger.info("close oracleConnection");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

Java并发编程的艺术笔记(四)——ThreadLocal的使用的更多相关文章

  1. java并发编程的艺术——第四章总结

    第四章并发编程基础 4.1线程简介 4.2启动与终止线程 4.3线程间通信 4.4线程应用实例 java语言是内置对多线程支持的. 为什么使用多线程: 首先线程是操作系统最小的调度单元,多核心.多个线 ...

  2. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  3. Java并发编程的艺术笔记(五)——Java中的锁

    一.Lock接口的几个功能: 显示的获取和释放锁 尝试非阻塞的获取锁 能被中断的获取锁 超时获取锁 使用方式: Lock lock = new ReentrantLock(); lock.lock() ...

  4. Java并发编程的艺术笔记(七)——CountDownLatch、CyclicBarrier详解

    一.等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作,像加强版的join.(t.join()是等待t线程完成) 例: (1)开启多个线程 ...

  5. Java并发编程的艺术笔记(九)——FutureTask详解

    FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable实现的,多用于耗时的计算. 一.FutureTask的三种状态 二.get()和cancel()执行示意 三.使用 一 ...

  6. Java并发编程的艺术笔记(二)——wait/notify机制

    一.概述 一个线程修改了一个对象的值,另一个线程感知到变化从而做出相应的操作.前者是生产者,后者是消费者. 等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用 ...

  7. Java并发编程的艺术· 笔记(1)

    目录 1.volatile的原理 2.Synchonized 3.无锁-偏向锁-轻量级锁-重量级锁 4.Java实现原子操作 1.volatile的原理 如何保持可见性: 1)将当前处理器缓存行的数据 ...

  8. java并发编程的艺术(四)---ConcurrentHashMap原理解析

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  9. Java并发编程的艺术笔记(八)——线程池

    一.线程池的主要处理流程 ThreadPoolExecutor执行execute方法分下面4种情况. 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获 ...

随机推荐

  1. Hive 教程(六)-Hive Cli

    hive 有两种启动方式,一种是 bin/hive,一种是 hiveserver2, bin/hive 是 hive 的 shell 模式,所有任务在 shell 中完成,shell 就相当于 hiv ...

  2. CentOS 7 安装ActiveMQ

    今天给大家介绍一下 CentOS 7 下如何安装ActiveMQ,每个步骤均为亲自己操作后记录.下面我们开始吧. 1.首先进入自己的目录下面,创建并进入 activeMQ 文件夹(mkdir acti ...

  3. windows下使用zookeeper

    windows下dos窗口操作:https://blog.csdn.net/a632189007/article/details/78085858

  4. abap 优化之ST05

    DATA: gt_mara TYPE TABLE OF mara. SELECT * INTO TABLE gt_mara FROM mara WHERE MATKL = 'L000001' %_hi ...

  5. Docker搭建Gitlab服务器

    1.使用docker搜索gitlab镜像 docker search gitlab 2.下载镜像: docker pull docker.io/gitlab/gitlab-ce 3.查看docker镜 ...

  6. Hive分区表创建、分类

    一.分区表创建与说明 必须在表定义时创建partition a.单分区建表语句:create table day_table (id int, content string) partitioned ...

  7. shell判断文件,目录是否存在或者具有权限

    shell判断文件,目录是否存在或者具有权限  #!/bin/sh  myPath="/var/log/httpd/"  myFile="/var /log/httpd/ ...

  8. EfficientNet学习笔记

    EfficientNet是谷歌大脑在2019年提出的,论文地址是:https://arxiv.org/pdf/1905.11946.pdf 这篇文章主要想解决的一个问题是,如何平衡网络的深度.宽度和分 ...

  9. linux centos 7安装 apache php 及mariadb

    1安装Apache, PHP, MySQL以及php库组件. yum -y install httpd php mysql  php-mysql 2 安装apache扩展 yum -y install ...

  10. 10个不为人知的 Python 冷知识

    转载: 1. 省略号也是对象 ...这是省略号,在Python中,一切皆对象.它也不例外. 在 Python 中,它叫做 Ellipsis . 在 Python 3 中你可以直接写…来得到这玩意. 而 ...