前言:工作中将要使用ThreadLocal,先学习总结一波。有不对的地方欢迎评论指出。

定义

  ThreadLocal并不是一个Thread,而是Thread的局部变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。

作用

  实现每一个线程都有自己的共享变量。

使用方法

  

  initialValue:返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的,默认就是null。

  remove方法:将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 1.5 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

源码解析

  ThreadLocal 构造方法:

  

  initialValue() 方法:

  

  get() 方法:

  

  第一行为获取线程的当前活动线程

  

  然后获取到当前线程的ThreadLocalMap 对象,再通过当前threadLocal来获取这个map对象的键值对,从而取出当前threadLocal中存的变量副本。

  如果ThreadLocalMap 对象为空,或这个map里面还没有存当前threadLocal的变量副本,则调用setInitialValue(); 

  set() 方法:

  

  如果当前线程里面有线程变量map,则给当前线程变量(this)设置值(value);如果没有,则创建当前线程的线程变量map,并设置值。

  getMap() 方法:

  

  

  getMap() 方法中,返回了当前线程的变量,threadLocals,类型为ThreadLocalMap。

  setInitialValue() 方法:

  

  setInitialValue() 方法,主要是设置初始化的 当前线程变量的变量副本。如果当前线程里面还没有 当前线程变量Map(ThreadLocalMap),则,初始化当前线程(thread)的线程变量Map

  createMap() 方法:

  

  初始化当前线程(thread)的线程变量Map

  ThreadLocalMap 内部类:

  

  构造方法:

  

  从这里可以看出,threadLocalMap里面存的key值就是 ThreadLocal 对象。

  remove() 方法:

  

举例 验证线程变量的隔离性

 /**
* 本地线程变量 test
* Created by yule on 2018/6/26 22:35.
*/
public class ThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start(); Thread.sleep(100); ThreadDemo2 threadDemo2 = new ThreadDemo2();
threadDemo2.start(); ThreadLocalTools.stringThreadLocal.set("main设置值");
System.out.println(ThreadLocalTools.stringThreadLocal.get());
}
} class ThreadLocalTools{
public static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
} class ThreadDemo1 extends Thread{
@Override
public void run() {
super.run();
for(int i = 0; i < 10; i++){
System.out.println(ThreadLocalTools.stringThreadLocal.get());
ThreadLocalTools.stringThreadLocal.set("ThreadDemo1设置值");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class ThreadDemo2 extends Thread{
@Override
public void run() {
super.run();
for(int i = 0; i < 10; i++){
System.out.println(ThreadLocalTools.stringThreadLocal.get());
ThreadLocalTools.stringThreadLocal.set("ThreadDemo2设置值");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

  输出的第一个为null是因为在set()方法前调用get()方法,会给出initialValue()方法的值,默认为null。

总结

  ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

  ThreadLocal通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。

  ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中定义了一个ThreadLocalMap,每一个Thread中都有一个该类型的变量——threadLocals——用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

  通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值。

Java 类 ThreadLocal 本地线程变量的更多相关文章

  1. Java Concurrency - ThreadLocal, 本地线程变量

    共享数据是多线程应用最常见的问题之一,但有时我们需要为每个线程保存一份独立的变量.Java API 提供了 ThreadLocal 来解决这个问题. 一个 ThreadLocal 作用的例子: imp ...

  2. ThreadLocal本地线程变量的理解

     一般的Web应用划分为展现层.服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用.在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程.       ...

  3. 假如java类里的成员变量是自身的对象

    假如java类里的成员变量是自身的对象,则新建该类对象时内存中怎么分配空间,我感觉似乎死循环了. 不过我想的肯定是错的,因为很多类的成员变量是自身对象,并且绝对无错,举个例子: Class A{ pr ...

  4. java类里的成员变量是自身的对象问题

    今晚看单例模式饿汉时想到一个问题:假如java类里的成员变量是自身的对象,则新建该类对象时内存中怎么分配空间,我感觉似乎死循环了.于是上网搜索了下,哈哈,果然有人早就思考过这个问题了,站在巨人的肩膀上 ...

  5. ThreadLocal = 本地线程?

    一.定义 ThreadLocal是JDK包提供的,从名字来看,ThreadLocal意思就是本地线程的意思. 1.1 是什么? 要想知道他是个啥,我们看看ThreadLocal的源码(基于JDK 1. ...

  6. 测试 Java 类的非公有成员变量和方法

    引言 对于软件开发人员来说,单元测试是一项必不可少的工作.它既可以验证程序的有效性,又可以在程序出现 BUG 的时候,帮助开发人员快速的定位问题所在.但是,在写单元测试的过程中,开发人员经常要访问类的 ...

  7. Flask中的ThreadLocal本地线程,上下文管理

    先说一下和flask没有关系的: 我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程对同一块 ...

  8. java类中,成员变量赋值第一个进行,其次是静态构造函数,再次是构造函数

    如题是结论,如果有人问你Java类的成员初始化顺序和初始化块知识就这样回答他.下面是代码: package com.test; public class TestClass{ // 成员变量赋值第一个 ...

  9. Java类中各种静态变量的加载顺序的学习

    最近在补<thinking in java>...有一节提到了加载类需要做的一些准备...我照着书本敲了一下代码...同时稍微修改了一下书本上的代码.... package charpte ...

随机推荐

  1. 虚拟机搭建ftp环境

    引用http://www.cnblogs.com/xiangxiaodong/archive/2013/12/23/3487028.html,学习. 本人是在windows8系统下,Oracle VM ...

  2. tf入门-卷积步长strides参数的具体解释

    conv1 = tf.nn.conv2d(input_tensor,conv1_weights,strides=[1,1,1,1],padding='SAME') 这是一个常见的卷积操作,其中stri ...

  3. python------对于面向对象的理解

    python中一切皆为对象 其实面向对象没什么高大上的东西,只不过把我们平时对于事物的描述和动作系统的总结成了一个定义事物的方法而已. 我们平时向别人介绍一个他(她)从未见过的东西,会从外形和外貌特征 ...

  4. 关于Mysql数据库查询数据大小写的问题汇总

    前天在问答区看到一个童鞋对于mysql中大小写问题不熟悉,在回复他后再次汇总梳理如下: mysql中大小写问题主要有以下两种: A.表名区分大小写 ower_case_table_names 是表名区 ...

  5. leetcode-79-单词搜索(用dfs解决)

    题目描述: 给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格.同一个单元格内的字母不允许 ...

  6. javascript如何阻止事件冒泡和默认行为

    阻止冒泡:    冒泡简单的举例来说,儿子知道了一个秘密消息,它告诉了爸爸,爸爸知道了又告诉了爷爷,一级级传递从而以引起事件的混乱,而阻止冒泡就是不让儿子告诉爸爸,爸爸自然不会告诉爷爷.下面的demo ...

  7. Spring AOP 概述

    1. AOP的概念 AOP 是Aspect-Oriented Programming(面向方面编程或者面向切面)的简称,维基百科对其解释如下: Aspect是一种新的模块化机制,用来描述分散在对象.类 ...

  8. linux之getenv putenv setenv和unsetenv详解

    1.getenv函数 头文件:#include<stdlib.h> 函数原型: char * getenv(const char* name); 函数说明:getenv()用来取得参数na ...

  9. TX2 用文件IO的方式操作GPIO

    概述 通过 sysfs 方式控制 GPIO,先访问 /sys/class/gpio 目录,向 export 文件写入 GPIO 编号,使得该 GPIO 的操作接口从内核空间暴露到用户空间,GPIO 的 ...

  10. SpringMVC初写(四)上传和下载功能的实现

    一.文件上传 流程: 导入包commons-fileuplad组件和依赖包commons-io组件 配置springmvc支持上传的组件: 启动SpringMVC注解支持 配置上传解释器 构建一个上传 ...