java核心-多线程(9)- ThreadLocal类
1.背景
ThreadLocal类我想一般的码农或初级程序员在平时开发中基本上接触不到,但是面试老师会问、往高级点走会遇到这个类。这个类不是为了解决资源的竞争问题,而是为每个线程提供同一个容器,容器内部为每个线程提供一块空间,各个线程只能操作自己的空间,相互之间独立。
这个类在java.lang包中。
2.使用示例
废话不多说,直接先看一下简单使用demo
public class ThreadLocalTest {
private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>();
public void setThreadLocal(List<String> value){
threadLocal.set(value);
}
public void getThreadLocal(){
List<String> strings = threadLocal.get();
strings.forEach(value -> System.out.println(Thread.currentThread().getName() + "###" + value));
}
public static void main(String[] args){
System.out.println("test threadlocal");
ThreadLocalTest threadLoaclTest = new ThreadLocalTest();
new Thread(new Runnable() {
@Override
public void run() {
ArrayList<String> strings = new ArrayList<>();
strings.add("1");
strings.add("2");
strings.add("3");
threadLoaclTest.setThreadLocal(strings);
threadLoaclTest.getThreadLocal();
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
ArrayList<String> strings = new ArrayList<>();
strings.add("a");
strings.add("b");
strings.add("c");
threadLoaclTest.setThreadLocal(strings);
threadLoaclTest.getThreadLocal();
}
},"t2").start();
}
//ThreadLocal类型变量表面被多个线程使用,每个线程设置互相之间没有影响。因为它内部有一个ThreadLocalMap类型的变量映射了线程和它所设定的值。
/*
执行结果
t1###1
t1###2
t1###3
t2###a
t2###b
t2###c
*/
}
3.一些和threadlocal相关的面试总结,参考https://www.jianshu.com/p/3f0f9194d658
11)什么是线程局部变量?
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本,是线程隔离的。线程隔离的秘密在于ThreadLocalMap类(ThreadLocal的静态内部类)
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
ThreadLocal的方法:void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何为每个线程创建变量的副本的:
首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
总结:
a、实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的
b、为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
c、在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法(这一条是错误的,不会报错,而是返回null)
4.简单看一下源码
ThreadLocal源码分析
1.public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //1.获取当前线程的ThreadLocalMap类型成员变量
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //2.这个map中可能存放多个ThreadLocal类型变量,map存放key就是ThreadLocal对象本身,而value就是ThreadLocal<T>的T类型数值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //3.每个线程对象中的ThreadLocalMap类型变量threadlocals是整个设计的灵魂,和面试官吹就好了
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null; //4.如果在set前,get的话得到的就是null
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
3.public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
//所以提到ThreadLocal类,在源码层面应该立即想到:在Thread类中有个ThreadLocal.ThreadLocalMap类型的变量threadlocals.
5.ThreadLocal的应用场景
这是最重要的,非常重要的,学的再好,不会用,甚至不知道用在哪,学来何用,不就是浪费精力吗?前面说过对于码农而言,在实际开发过程中不容易用到这个东西,如果你能清楚知道它用在什么地方,那么是不是就高级一点,这就是可以提高的地方。
1.spring对有状态bean的管理
Spring使用ThreadLocal解决线程安全问题。通常只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全的“状态性对象”采用ThreadLocal进行封装,让它们也成为线程安全的“状态性对象”,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。参考:https://www.jianshu.com/p/f956857a8304
java核心-多线程(9)- ThreadLocal类的更多相关文章
- java核心-多线程(4)-线程类基础知识
1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...
- Java并发多线程 - 并发工具类JUC
安全共享对象策略 1.线程限制 : 一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改 2.共享只读 : 一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问, 但是任何线程都 ...
- java核心-多线程-Java多线程编程涉及到包、类
Java有关多线程编程设计的类主要涉及两个包java.lang和java.util.concurrent两个包 java.lang包,主要是线程基础类 <1>Thread <2> ...
- Java并发编程之ThreadLocal类
ThreadLocal类可以理解为ThreadLocalVariable(线程局部变量),提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回当 ...
- 编写Java程序,使用ThreadLocal类,项目中创建账户类 Account,类中包括账户名称name、 ThreadLocal 类的引用变量amount,表示存款
查看本章节 查看作业目录 需求说明: 某用户共有两张银行卡,账户名称相同,但卡号和余额不同.模拟用户使用这两张银行卡进行消费的过程,并打印出消费明细 实现思路: 项目中创建账户类 Account,类中 ...
- Java并发基础07. ThreadLocal类以及应用技巧
在前面的文章(6. 线程范围内共享数据)总结了一下,线程范围内的数据共享问题,即定义一个 Map,将当前线程名称和线程中的数据以键值对的形式存到 Map 中,然后在当前线程中使用数据的时候就可以根据当 ...
- java核心-多线程-线程类-Callable、Future和FutureTask
基本概念 <1>Callable,Callable和Runnable差不多,两者都是为那些其实例可能被另一个线程执行的类而设计的,最主要的差别在于Runnable不会 返回线程运算结果,C ...
- java核心-多线程(6)-线程池-ThreadPoolExecutor
1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent 2.使用示例 demo1 public class ThreadPoolDemo { ...
- java核心-多线程(8)- 并发原子类
使用锁能解决并发时线程安全性,但锁的代价比较大,而且降低性能.有些时候可以使用原子类(juc-atomic包中的原子类).还有一些其他的非加锁式并发处理方式,我写这篇文章来源于Java中有哪些 ...
随机推荐
- map中entrySet和KeySet的区别
- [转]Shell编程
原文链接 Shell编程其实真的很简单(一) 如今,不会Linux的程序员都不意思说自己是程序员,而不会shell编程就不能说自己会Linux.说起来似乎shell编程很屌啊,然而不用担心,其实she ...
- spring boot 中 2.X 的跨域请求
解决跨域: @Configuration @EnableAutoConfiguration public class ZooConfiguration { @Bean public FilterReg ...
- springboot不能加载主类
参考:https://www.cnblogs.com/iyyy/p/8085719.html 若为maven项目:用maven更新下项目.
- Duilib热键
转载:https://www.zhaokeli.com/article/8288.html 在initwindow中注册热键 //生成热键标识,需要保存起来退出时销毁使用 int m_HotKeyId ...
- Flask - 上下文管理(核心)
参考 http://flask.pocoo.org/docs/1.0/advanced_foreword/#thread-locals-in-flask https://zhuanlan.zhihu. ...
- SQLite、MySQL和PostgreSQL 三种关系数据库哪个好?
关系型数据库的使用已经有相当长的时间了.它们变得流行起来托了管理系统的福,关系模型被实现得相当的好,并且被证明是操作数据的好方法(特别是事务性强的应用). 在这篇DigitalOcean文章中,我们将 ...
- mybatis关键查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- QQ企业通--客户端登陆模块设计---知识点2
Maximizable 属性 获取一个值,该值指定窗口是否可以最大化. fromBorderstyle 成员名称 说明 None 无边框. Fi ...
- OSI参考模型对网络排错的指导
问题 当我们遇到网络故障的时候,比如连不上网.打开浏览器无法正常访问等问题的时候,我们应该怎么排查呢? 我们首先想到的是物理层,因为在OSI参考模型中物理层是在最低端.最基础. 物理层排查 主要查看连 ...