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并发编程. ...
随机推荐
- UCOS阅读问题累积
1.#ifdef __cplusplus extern "C" { #endif 作用: 一般用于将C++代码以标准C形式输出(即以C的形式被调用),这是因为C++虽然常被认 ...
- sql修改数据库中表的结构
ALTER TABLE TableName1 ADD | ALTER [COLUMN] FieldName1 FieldType [(nFieldWidth [, nPrecision])] [NUL ...
- react系列(一)JSX语法、组件概念、生命周期介绍
JSX React中,推出了一种新的语法取名为JSX,它给了JS中写HTML标签的能力,不需要加引号.JSX的语法看起来是一种模板,然而它在编译以后,会转成JS语法,只是书写过程中的语法糖. JSX的 ...
- ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener
执行$ORACLE_HOME/bin/dbstart 启动数据库提示如下: [oracle@prim bin]$ ./dbstart ORACLE_HOME_LISTNER is not SET, u ...
- HTML基础之常用标签
Meta 标签介绍 Meta的属性有两种:name和http-equiv name属性用于描述网页,对应于content <meta name="Generator" con ...
- CF1066D Boxes Packing(二分答案)
题意描述: 你有$n$个物品,每个物品大小为$a_i$,$m$个盒子,每个盒子的容积为$k$.$Maksim$先生想把物品装入盒子中.对于每个物品,如果能被放入当前的盒子中,则放入当前盒子,否则换一个 ...
- java8的新特性,Collections.sort(排序的List集合)的使用,对list封装Map里面的某个值进行排序
--------------------------对简单list的排序---------------------------------- List<Integer> list = ne ...
- openwrt procd启动流程和脚本分析
Linux内核执行start_kernel函数时会调用kernel_init来启动init进程,流程如下图: graph LR A[start_kernel] -->B(rest_init) B ...
- ruby 批量下载王者荣耀皮肤
主要采用ruby Parallel库提供的多线程方式: require 'unirest' require 'open-uri' require 'parallel' require 'json' u ...
- Python 爬虫 (一)
爬: 爬一个网站需要几步? 确定用户的需求 根据需求,寻找网址 读取网页 urllib request requests 定位并提取数据 正则 xpath beautiful soup 存储数据 my ...