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. java执行bat代码

    java执行bat代码.txt public static void runbat(String path,String filename) { String cmd = "cmd /c s ...

  2. 多表表与表关系 增删改查 admin

    今日内容 多表表与表关系 增删改查表数据 admin 多表操作 表与表关系 默认指向主键 可能是隐藏主键 djamgo1.1默认级联(models. SET NULL解除级联) 一对一 先建立少的一方 ...

  3. RabbitMQ 示例-生产者-消费者-direct-topic-fanout

    这是生产者和消费者2个项目, 包含 direct,topic,fanout模式下的消费,springboot + rabbitmq 代码地址:https://github.com/duende99/R ...

  4. 在web项目中配置log4j

    在web.xml中添加如下代码 <context-param> <param-name>contextConfigLocation</param-name> < ...

  5. AGC009E Eternal Average

    atc 神题orz 那个擦掉\(k\)个数然后写上一个平均值可以看成是\(k\)叉Huffman树的构造过程,每次选\(k\)个点合成一个新点,然后权值设为平均值.这些0和1都会在叶子的位置,同时每个 ...

  6. 解决:使用java连接Fastdfs,上传文件时报:SocketTimeOutException的问题

    最近研究了下分布式存储Fastdfs,在centOS上配置完后,使用centOS或putty连接并上传图片,然后用浏览器读取storage server返回的URL,一切正常. 但是,使用eclips ...

  7. postgres日常操作

    1.启动pgsl数据库 [postgres@master ~]$ pg_ctl start [postgres@master data]$ pg_ctl -D /usr/local/pgsql/dat ...

  8. 线程安全的Singleton要点

    1.privat static Singleton 要加votatile关键字修饰,防止对象的初始化代码与引用赋值代码进行重排序. 2.getInstance方法,最外层要加if (instance ...

  9. Delphi 有类型文件

  10. centos 7 安装 redis-5.0.5

    [root@localhost ~]# yum -y install gcc make [root@localhost ~]# wget http://download.redis.io/releas ...