一、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的更多相关文章

  1. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  2. java并发基础及原理

    java并发基础知识导图   一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...

  3. java并发基础(五)--- 线程池的使用

    第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...

  4. java并发基础(二)

    <java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...

  5. Java并发基础概念

    Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...

  6. 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

    本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...

  7. java并发基础(一)

    最近在看<java并发编程实战>,希望自己有毅力把它读完. 线程本身有很多优势,比如可以发挥多处理器的强大能力.建模更加简单.简化异步事件的处理.使用户界面的相应更加灵敏,但是更多的需要程 ...

  8. Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

    AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...

  9. Java并发基础:进程和线程之由来

    转载自:http://www.cnblogs.com/dolphin0520/p/3910667.html 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程. ...

随机推荐

  1. ARP, Fragmentation and Reassembly

    Address Resolution Protocol IP addresses are said to be logical, because they are defined in terms o ...

  2. iOS8以后UIAlertView和UIActionSheet两种alert页面都将通过UIAlertController来创建

    1. Important: UIAlertView is deprecated in iOS 8. (Note that UIAlertViewDelegate is also deprecated. ...

  3. VS 2017 + EF6 + MySQL5.7 建立实体模型闪退问题

    具体环境是:VS2017 ..NET Framework 4.6 .MySql.Data.Entity 6.9.12 在这个环境下总是不成功,具体是在这一步闪退,也不报错: 在点击“下一步”后,没有进 ...

  4. HDBS之应用代码优化

    一.目录结构树 总体概述 代码检测工具sonar HDBS代码优化 总结开发注意点 二.总体概述 进入现在这家公司我的第一个任务就是对HDBS进行代码质量优化.HDBS可能大家不是很了解,现在给大家简 ...

  5. python 爬虫简介以及使用方法

    阶段大纲: 一. 爬虫 1. 基本操作 - 登录任意网站(伪造浏览器的任何行为) 2. 性能相关 - 并发方案: - 异步IO: gevent/Twisted/asyncio/aiohttp - 自定 ...

  6. ubuntu 18.04可以连接内网,无法连接外网

    手动增加网关后,又重新sudo apt-get upgrade,  提示/etc/resolvconf/resolv.conf.d更新时,选Y后,不用手动修改网关也可以连接外网了. 一切默认更新后,1 ...

  7. 判断FreeMarker是否为空

    转自:https://blog.csdn.net/lwt976647637/article/details/73135933 (1)判断Map数据是否为空 <#ifmaster??&&a ...

  8. Echarts+百度地图

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Linux入门——vsftpd

    vsftpd Introduction vsftpd是一款在Linux发行版中最受推崇的FTP服务器程序.特点是小巧轻快,安全易用. vsftpd 的名字代表"very secure FTP ...

  10. LCD驱动程序编写

    学习目标:编写LCD驱动程序,熟悉根据芯片手册分析时序图,配置寄存器,并测试LCD程序. 一.LCD驱动程序编写 步骤: 1)分配fb_info结构体 2)设置fb_info结构体 a. 固定参数 b ...