Java并发基础--ThreadLocal
一、ThreadLocal定义
ThreadLocal是一个可以提供线程局部变量的类,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路,通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量(比如本次请求的用户信息),减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
二、基本使用
public class ThreadLocalDemo {
public static ThreadLocal<Person> local = new ThreadLocal<Person>(){
protected Person initialValue() {
return new Person(Thread.currentThread().getName());
};
};
public static Person getPerson(){
return local.get();
}
public static void main(String[] args) {
for(int i=0;i<5;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName());
}
}).start();
}
System.out.println(Thread.currentThread().getName()+":"+ThreadLocalDemo.getPerson().getName());
}
}
class Person{
private String name;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
结果输出:
main:main
Thread-0:Thread-0
Thread-2:Thread-2
Thread-4:Thread-4
Thread-1:Thread-1
Thread-3:Thread-3
从结果可以看出,local对象针对不同的线程提供的Person变量是不同的。并且互不影响。同一个线程能够共享local中保存的对象。
三、源码分析
1.get()方法,用来获取当前调用get方法的线程对应在ThreadLocal中保存的变量的副本。
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//通过线程获取对应的ThreadLocalMap,ThreadLocalMap是ThreadLocal中的一个静态内部类,可将他看做一个特殊的map,key是ThreadLocal,value则是保存的变量的值。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
通过线程t获取,在Thread类中定义了ThreadLocal.ThreadLocalMap threadLocals = null;即将线程和ThreadLocalMap联系起来,初始时,threadLocals=null,则需要通过setInitialValue方法创建,具体如下:
private T setInitialValue() {
T value = initialValue();//调用ThreadLocal中的initialValue方法获取变量的初始值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);//当线程关联的ThreadLocalMap为null的时候,创建一个ThreadLocalMap,并赋值让线程t的threadLocals引用指向新创建的ThreadLocalMap对象。
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
通过ThreadLocal的源码分析,可以知道每个线程都有一个ThreadLocalMap类型的引用threadLocals,线程初始时候为null,当我们调用set或者get方法的时候,通过当前线程获取ThreadLocalMap,如果不为null,则通过当前threadLocal作为key,获取对应的value值。如果为null,则new ThreadLocalMap(this, firstValue);,this指向ThreadLocal对象,firstValue则是initialValue()方法中指定的初始值。
2.set()方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//通过线程类Thread获取关联的ThreadLocalMap对象,如果不为null,直接调用ThreadLocalMap的set方法,设置键值对,key为ThreadLocal对象,value则为设置的值。
3.remove()方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
四、应用场景
数据库连接,session管理,redis连接等。例如经典的使用,如下所示:
public class JdbcTemplateImpl {
// 数据库用户名
private String username;
// 数据库密码
private String password;
// 驱动信息
private String driver;
// 数据库地址
private String url;
// 声明线程共享变量
public static ThreadLocal<Connection> connections= new ThreadLocal<Connection>();
public JdbcTemplateImpl(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
try {
Class.forName(this.driver);
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection getConnection() {
Connection connection = connections.get();
try {
if (connection != null && !connection.isClosed()) {
return connection;
}else {
connection = DriverManager.getConnection(this.url, this.username, this.password);
connections.set(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
Java并发基础--ThreadLocal的更多相关文章
- Java 并发基础
Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...
- java并发基础及原理
java并发基础知识导图 一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...
- java并发基础(五)--- 线程池的使用
第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...
- java并发基础(二)
<java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...
- Java并发基础概念
Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...
- 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!
本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...
- java并发基础(一)
最近在看<java并发编程实战>,希望自己有毅力把它读完. 线程本身有很多优势,比如可以发挥多处理器的强大能力.建模更加简单.简化异步事件的处理.使用户界面的相应更加灵敏,但是更多的需要程 ...
- Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)
AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...
- Java并发基础:进程和线程之由来
转载自:http://www.cnblogs.com/dolphin0520/p/3910667.html 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程. ...
随机推荐
- Android 进价5_自定义广播 通过广播更新ListView的适配器 下载管理
1.在处理下载管理时,服务在后台运行,下载完成后要更新listview列表的按钮,将“下载”改成“打开”这样一个功能. 在Activity里面写一个静态内部类,继承广播.其中属性text_button ...
- @class CLASS ;必须加分号,且卸载@interface之前
1. @class CLASS ;必须加分号,且卸载@interface之前 示例代码如下: #import <UIKit/UIKit.h> @class WZProduct; //注意 ...
- sql server 中判断分组排序的使用示例
现在需要查询一组数据,是对一列字段(column01)的数据分范围查询后分组排序: select (case when [column01] >0 AND [column01]<= 500 ...
- NDK-C++ support
1.NDK相关各种可用的C++运行库Android平台自带微型C++运行库(system),NDK提供补充功能的C++运行库(gabi++, stlport, gnustl)运行库 异常支持 RTTI ...
- mui的openWindowWithTitle()参数及说明
mui.openWindowWithTitle({ url: 'xxx.html', //String类型,要打开的界面的地址 id: 'id', //String类型,要打开的界面的id style ...
- 『ACM C++』 Codeforces | 1066A - Points in Segments
大一生活真 特么 ”丰富多彩“ ,多彩到我要忙到哭泣,身为班长,很多班级的事情需要管理,也是,什么东西都得体验学一学,从学生会主席.团委团总支.社团社长都体验过一番了,现在差个班长也没试过,就来体验了 ...
- MySQL数据库修改数据表类型(引擎)的方法
MySQL数据库使用事务,相关数据表必须为InnoDB引擎 查看数据表状态: SHOW TABLE STATUS FROM wawa WHERE NAME='ww_invite_code_temp'; ...
- MySQL/MariaDB学习笔记——mysql.user表中存在多个root用户问题理解
mysql.user表中存在多个root用户问题 问题描述:使用 SELECT host,user FROM mysql.user 发现mysql.user表中存在三个root用户,如下 持着对中几个 ...
- TinyMCE:下载、安装、配置
第一步:下载 官网下载:https://www.tiny.cloud/download/ TinyMCE从4.0开始,不再支持直接下载,而是直接使用提供免费的CDN,让用户免除安装过程,可以在网站中使 ...
- Vue解决接口访问跨域问题
随手摘录 Vue解决接口访问跨域问题 1.打开 config -> index.js 2. 找到proxyTable 3.粘贴 如下代码,'https://www.baidu.com'换成要访问 ...