Java并发编程的艺术笔记(四)——ThreadLocal的使用
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的使用的更多相关文章
- java并发编程的艺术——第四章总结
第四章并发编程基础 4.1线程简介 4.2启动与终止线程 4.3线程间通信 4.4线程应用实例 java语言是内置对多线程支持的. 为什么使用多线程: 首先线程是操作系统最小的调度单元,多核心.多个线 ...
- 多线程的通信和同步(Java并发编程的艺术--笔记)
1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递. 2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...
- Java并发编程的艺术笔记(五)——Java中的锁
一.Lock接口的几个功能: 显示的获取和释放锁 尝试非阻塞的获取锁 能被中断的获取锁 超时获取锁 使用方式: Lock lock = new ReentrantLock(); lock.lock() ...
- Java并发编程的艺术笔记(七)——CountDownLatch、CyclicBarrier详解
一.等待多线程完成的CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作,像加强版的join.(t.join()是等待t线程完成) 例: (1)开启多个线程 ...
- Java并发编程的艺术笔记(九)——FutureTask详解
FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable实现的,多用于耗时的计算. 一.FutureTask的三种状态 二.get()和cancel()执行示意 三.使用 一 ...
- Java并发编程的艺术笔记(二)——wait/notify机制
一.概述 一个线程修改了一个对象的值,另一个线程感知到变化从而做出相应的操作.前者是生产者,后者是消费者. 等待/通知机制,是指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用 ...
- Java并发编程的艺术· 笔记(1)
目录 1.volatile的原理 2.Synchonized 3.无锁-偏向锁-轻量级锁-重量级锁 4.Java实现原子操作 1.volatile的原理 如何保持可见性: 1)将当前处理器缓存行的数据 ...
- java并发编程的艺术(四)---ConcurrentHashMap原理解析
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
- Java并发编程的艺术笔记(八)——线程池
一.线程池的主要处理流程 ThreadPoolExecutor执行execute方法分下面4种情况. 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获 ...
随机推荐
- Codeforces 1228C. Primes and Multiplication
传送门 当然是考虑 $n$ 的每个质数 $p$ 对答案的贡献 考虑 $p^k$ 在 $[1,m]$ 中出现了几次,显然是 $\left \lfloor \frac{m}{p^k} \right \rf ...
- Codeforces 1201C. Maximum Median
传送门 看到中位数考虑先把数排序一下 然后有个显然的贪心,一个数增加后一定不能比下一个数大,不然我们直接增加下一个数显然更优 所以初始时的中位数操作后也是中位数 那么我们只要考虑中间再往后怎么加使得答 ...
- 【From StackOverFlow】--set-upstream 和--set-upstream-to=以及--track的区别
本文引自StackOverFlow http://stackoverflow.com/questions/26090689/git-set-upstream-to-vr-track
- KPI VS OKR
近几年,OKR 这个词越来越流行了. 在硅谷,Google.Facebook.Amazon.LinkedIn 等公司都陆续成功落地了 OKR,国内的互联网巨头们,腾讯.百度.滴滴.小米等互联网公司也都 ...
- Python基础教程之dict和set
1. dict Python中的dict等于js中的 map ,使用键-值(key-value)存储,具有极快的查找速度. 如果 我们要根据同学的姓名去查找他的成绩在不用dict的情况下.就需要两个l ...
- java复制文件范例代码
String url1 = "F:\\SuperMap-Projects\\region.udb";// 源文件路径 try { for(int i=1;i<101;i++) ...
- 多个类用@feignclient标注同一个服务,出错问题:Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.Caused by: org.springframework.beans.factory.support.Bea..
如果标注了两个或以上类 @FeignClient 标注同一个 服务名称 调用方会主配置类启动会报错 测试类报错 java.lang.IllegalStateException: Failed to l ...
- 服务命令(systemctl的使用)
常用的service与systemctl命令的对比 应用举例: ●start:开启服务 ●stop:停止服务 ●status:参数来查看服务运行情况 ●restart:重新加载服务 应用举例·: #启 ...
- 21、Nginx 常见问题
1.多个server_name容易产生冲突,会按照如下顺序匹配 1.首先选择所有的字符串完全匹配的server_name.(完全匹配) 2.选择通配符在前面的server_name,如*.bgx.co ...
- 5.(基础)tornado异步
终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...