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

  1. java核心-多线程(4)-线程类基础知识

    1.并发 <1>使用并发的一个重要原因是提高执行效率.由于I/O等情况阻塞,单个任务并不能充分利用CPU时间.所以在单处理器的机器上也应该使用并发. <2>为了实现并发,操作系 ...

  2. Java并发多线程 - 并发工具类JUC

    安全共享对象策略 1.线程限制 : 一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改 2.共享只读 : 一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问, 但是任何线程都 ...

  3. java核心-多线程-Java多线程编程涉及到包、类

    Java有关多线程编程设计的类主要涉及两个包java.lang和java.util.concurrent两个包 java.lang包,主要是线程基础类 <1>Thread <2> ...

  4. Java并发编程之ThreadLocal类

    ThreadLocal类可以理解为ThreadLocalVariable(线程局部变量),提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回当 ...

  5. 编写Java程序,使用ThreadLocal类,项目中创建账户类 Account,类中包括账户名称name、 ThreadLocal 类的引用变量amount,表示存款

    查看本章节 查看作业目录 需求说明: 某用户共有两张银行卡,账户名称相同,但卡号和余额不同.模拟用户使用这两张银行卡进行消费的过程,并打印出消费明细 实现思路: 项目中创建账户类 Account,类中 ...

  6. Java并发基础07. ThreadLocal类以及应用技巧

    在前面的文章(6. 线程范围内共享数据)总结了一下,线程范围内的数据共享问题,即定义一个 Map,将当前线程名称和线程中的数据以键值对的形式存到 Map 中,然后在当前线程中使用数据的时候就可以根据当 ...

  7. java核心-多线程-线程类-Callable、Future和FutureTask

    基本概念 <1>Callable,Callable和Runnable差不多,两者都是为那些其实例可能被另一个线程执行的类而设计的,最主要的差别在于Runnable不会 返回线程运算结果,C ...

  8. java核心-多线程(6)-线程池-ThreadPoolExecutor

    1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent 2.使用示例 demo1 public class ThreadPoolDemo { ...

  9. java核心-多线程(8)- 并发原子类

        使用锁能解决并发时线程安全性,但锁的代价比较大,而且降低性能.有些时候可以使用原子类(juc-atomic包中的原子类).还有一些其他的非加锁式并发处理方式,我写这篇文章来源于Java中有哪些 ...

随机推荐

  1. 标签UILabel的讲解

    首先,我先自定义几个名词,方便接下来的讲解工作.如下图所示: 接下来,通过五个方面来讲解我们能对UILabel做出哪些改变或者称之为设置: 1.文字 1.1普通文字:内容text.字体大小font.字 ...

  2. PTA的Python练习题(九)

    从 第3章-18 输出10个不重复的英文字母 继续 1. a=input() b='' c=a.replace(' ','') for i in c: if i not in b: b=b+i d=l ...

  3. 实用类-<装箱与拆箱>

    装箱:把基本数据类型装换为对应的对象类 作用:1.在需要使用对象类型的时候,装换成对应的对象类型(集合里面) 2.转换完成以后,拥有相应的属性和方法,方便咱们的数据操作 拆箱 Integer intO ...

  4. nacos集群配置

    一.    环境准备 Nacos 依赖 java环境来运行.如果您是从代码开始构建并运行Nacos,还需要为此配置 Maven环境,请确保是在以下版本环境中安装使用: 64 bit OS,支持 Lin ...

  5. 刷题19. Remove Nth Node From End of List

    一.题目说明 这个题目是19. Remove Nth Node From End of List,不言自明.删除链表倒数第n个元素.难度是Medium! 二.我的解答 链表很熟悉了,直接写代码. 性能 ...

  6. 零基础学完Python的7大就业方向,哪个赚钱多?

    “ 我想学 Python,但是学完 Python 后都能干啥 ?” “ 现在学 Python,哪个方向最简单?哪个方向最吃香 ?” “ …… ” 相信不少 Python 的初学者,都会遇到上面的这些问 ...

  7. 2020-2-18 restful的学习

    1-1 restful简介及资源的介绍 restful   是什么? 本质:一种软件架构风格 核心:面向资源     解决的问题: 1. 降低开发的复杂性 2. 提高系统的可伸缩性     设计概念和 ...

  8. OPC DA通讯 KEP6.4 DCOM 配置脚本

    在OPC DA通讯测试时总是避免不了要配置DCOM,我习惯是在虚拟机中装这些通讯测试软件,每次安装都需要再次手动配置,感觉很是麻烦 因此,查找资料,了解到可以用dcomperm这个软件来在命令行下进行 ...

  9. 七 MyBatis整合Spring,DAO开发(传统DAO&动态代理DAO)

    整合思路: 1.SQLSessionFactory对象应该放到Spring中作为单例存在 2.传统dao开发方式中,应该从Spring容器中获得SqlSession对象 3.Mapper代理行驶中,应 ...

  10. ORACLE 用32進制表示月中的一天

    WHEN 'YMD' THEN             v_year := to_char(SYSDATE, 'Y');             v_month := FN_CONVERT_DECIM ...