全面解析ThreadLocal类

Java线程与ThreadLocal:深入解析Thread类源码及ThreadLocal实现原理 本文的主要目的是深入解析Java中的Thread类源码和ThreadLocal的实现原理。我们将探讨Java线程模型的核心概念,分析Thread类的关键方法和属性,并详细说明ThreadLocal的工作机制。通过这种深入的分析,我们旨在帮助读者更好地理解Java并发编程的底层原理,从而在实际开发中更有效地使用这些工具。本文将首先介绍Java线程的基本概念,然后深入分析Thread类的源码,包括其重要的方法和属性。接着,我们将详细探讨ThreadLocal的实现原理,包括其内部数据结构和工作机制。 阅读详情

考虑有下面的工具类:

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUnits {
	private final static SimpleDateFormat sdf = new SimpleDateFormat(FormatType.TYPE1.type);
	
	public static String format(Date date){
		return sdf.format(date);
	}
	enum FormatType{
		TYPE1("yyyy-MM-dd HH:mm:ss"),
		TYPE2("yyyy-MM-dd");
		
		private String type;
		private FormatType(String type){
			this.type = type;
		}
		
		public String getType() {
			return type;
		}
	}
	
	public static void main(String[] args) {
		System.out.println(DateUnits.format(new Date()));
	}
}

我们知道,SimpleDateFormat的format方法不是线程安全的(主要是Calendar造成的),那么当多个线程使用这个工具时如何消除并发问题呢?当然很简单的一种方法,用sychronized即可,但是sychronized性能比较差,此时我们可以用到ThreadLocal类了,代码如下:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadLocalTest {
	private static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(){
		/*@Override
		protected SimpleDateFormat initialValue() {
			System.out.println("create simpledateFormat");
	       return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	    }*/
	};
	public static void main(String[] args) {
		ExecutorService ex = Executors.newCachedThreadPool();
		try{
			for(int i = 0;i < 5;i++){
				final int j = i;
				ex.execute(new Runnable(){
					public void run(){
						try {
							TimeUnit.SECONDS.sleep(j);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						System.out.println(tl.get().format(new Date()));
					}
				});
			}
		}finally{
			ex.shutdown();
		}
	}
}

结果:

create simpledateFormat
2018-03-25 20:56:12
create simpledateFormat
2018-03-25 20:56:13
create simpledateFormat
2018-03-25 20:56:14
create simpledateFormat
2018-03-25 20:56:15
create simpledateFormat
2018-03-25 20:56:16

每个线程都会创建自己的SimpleDateFormat对象。

下面详解一下ThreadLocal的实现机制

ThreadLocal常用方法有4个:set(T value)、get()、setInitialValue()、initialValue()

首先看一下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);
    }

当你向ThreadLocal中set元素时,该元素最终会存入当前线程对象的ThreadLocalMap中,通过查看Thread类的源码我们发现,Thread类中维护着一个ThreadLocal.ThreadLocalMap域:ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap是ThreadLocal类的一个静态内部类,其核心为一个Entry[]数组,这里的Entry不是HashMap里的Entry,而是 ThreadLocalMap类里的一个静态内部类,有些乱,贴出源码:

 static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

那么Thread的ThreadLocalMap成员什么时候初始化呢?这里其实也是懒加载,new Thread()并不会初始化,只有当你ThreadLocal时才会加载,看上面set方法的createMap方法便可得出此结论:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    } 
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

初始化大小16,每当Entry[]大小过2/3时会扩容,扩容会触发hash重排,这点倒是类似于HashMap(HashMap默认是超过3/4时扩容),set方法就介绍到此,总之set方法就是向当前线程对象的一个map里存放对象,由于key是ThreadLocal实例,所以你想让一个线程存放多个本地变量时,你不得不创建多个ThreadLocal对象

get方法:

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

get方法首先还是去找当前线程的ThreadLocalMap,如果找到了,然后就根据key(也就是调用get方法的ThreadLocal对象)的hash值通过运算定位到Entry[]数组中的具体元素:

 private Entry getEntry(ThreadLocal key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
如果ThreadLocalMap为空呢,get方法会返回什么?仔细看我的测试代码就可以发现,我并没有调用set方法,但是get方法依然得到了想要的SimpleDateFormat,我们可以看到get方法如果发现ThreadLocalMap还没有初始化时,不会马上返回null,而是去调用setInitialValue方法:
    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;
    }

setInitialValue方法又会去调用initialValue方法:

    protected T initialValue() {
        return null;
    }

看到这里我们可以发现默认情况下不先set而直接调用get方法的确行不通(经测试会报空指针异常),但是我们可以通过重写initialValue方法,而在现实中,我们的确应该这么做

ThreadLocal设计的精妙之处在于ThreadLocal本身不会存储任何数据,但是你要存取数据都要依赖它,遗憾的是现实开发中本人还没遇见很适合用ThreadLocal的场景(可能遇到了也没想到),不过毫无疑问它是有用武之地的,据说Spring的连接池模块很好的运用了ThreadLocal,有兴趣的同学可以深入研究一下







信息流广告ROI线性预测看板&投放分析监控看板展示&数据处理入库全流程 此看板数据量太大,在tableau public部署运行速度极慢,因此用图片作展示 数据清洗 阅读详情

相关推荐

Java 处理地理信息数据[DEM TIF文件数据获取高程]

EPSG 是一个国际标准,用于唯一标识地理坐标系和投影坐标系。

两点 1281

Java中的ThreadLocal深入解析

ThreadLocal 是 Java 提供的线程本地变量机制,每个线程拥有独立的变量副本。其核心实现是通过 Thread 类中的 ThreadLocalMap 存储数据,以 ThreadLocal 实例为 key,采用弱引用避免内存泄漏。但若使用后未调用 remove(),在线程池场景下仍可能导致内存泄漏,因为 value 是强引用。典型应用场景包括线程安全的数据隔离,但需注意及时清理避免资源浪费。

无论云泥意贯一 1263

别怕,“卷积”其实很简单

首先给大家讲一个关于卷积的小故事:小明是杭州某互联网大厂的员工,每天996十分辛苦,但小明最近爱上了打台球,经常不在工位。这天,小明的主管让小明改一个需求,小明却到楼下找产品部小丽打台球去了,被主管发现,他非常气愤,扇了小明一巴掌(注意,这就是输入信号,脉冲)。于是小明脸上会渐渐地(贱贱地)鼓起来一个包,小明的脸就是一个系统,而鼓起来的包就是小明的脸对巴掌的响应。好,这样就和信号系统建立起来意义对应的联系。

Machine Learning with Tutors 3183

全面理解ThreadLocal

目录 一、ThreadLocal简介 二、ThreadLocal的基本使用 三、ThreadLocal与Synchronized的区别 四、ThreadLocal的内部结构 五、ThreadLocal的核心方法源码 六、ThreadLocalMap的基本结构 七、ThreadLocal的内存泄露问题 八、ThreadLocalMap的hash冲突问题 一、ThreadLocal简介 从Java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问

weixiaohuai的博客 1029

从宁德新能源面试官视角,拆解Halcon/OpenCV图像处理工程师的硬核技能树(附避坑指南)

本文从宁德新能源面试官视角出发,详细拆解Halcon/OpenCV图像处理工程师的核心技能树,涵盖硬件选型、算法实现和系统工程思维三大能力维度。文章提供工业视觉工程师面试的实战避坑指南,包括硬件配置要点、算法优化技巧及职业发展路径,助力求职者系统提升图像处理专业技能。

weixin_28732953的博客 129

Win10系统权限深度解析:如何安全地将C盘所有者从TrustedInstaller变更为Administrators

本文深入解析了Win10系统中C盘文件权限管理的核心机制,解释了为何管理员账户(Administrators)默认无法修改由TrustedInstaller保护的系统文件。文章在强调安全风险的前提下,详细提供了通过图形界面和命令行两种方式,将文件或文件夹所有权从TrustedInstaller安全变更为Administrators的实操步骤,并给出了关键的风险预警与权限恢复方法。

weixin_29305313的博客 549

ThreadLocal全面解析

ThreadLocal 全面解析

h2806504347的博客 274

隐藏在WAV音频里的秘密:用Python脚本从‘噪音’CTF题中提取Flag

本文详细解析了如何利用Python脚本从WAV音频文件中提取隐藏的Flag信息,特别针对CTF比赛中的音频隐写技术。通过分析WAV文件结构、编写Python解析脚本,并解决字节序处理和异常值等实际问题,帮助读者掌握从‘噪音’中解码Flag的技巧。

weixin_30617737的博客 487

ThreadLocal详解

ThreadLocal 是一个存储线程本地副本的工具类。 要保证线程安全,不一定非要进行同步。同步只是保证共享数据争用时的正确性,如果一个方法本来就不涉及共享数据,那么自然无须同步。 Java 中的 无同步方案 有: 可重入代码 - 也叫纯代码。如果一个方法,它的 返回结果是可以预测的,即只要输入了相同的数据,就能返回相同的结果,那它就满足可重入性,当然也是线程安全的。 线程本地存储 - 使用 ThreadLocal 为共享变量在每个线程中都创建了一个本地副本,这个副本只能被当前线程访问,其他线程无法访问

学而不思则忘 776

海康视觉算法平台VisionMaster 4.3.0 C# 二次开发01 加载方案并获取结果

第一次使用海康视觉算法平台VisionMaster 4.3.0,项目中要使用这个平台进行视觉处理并获取结果。

StevenChen的博客 7878

通过Python实现物联网金橙子自动打标

对于自家公司使用金橙子打标卡时,为了与生产线或者PLC联动等,往往需要对金橙子打标卡进行二次开发,而在二次开发中不得不涉及到MarkEzd.dll这个文件,而这个dll文件网上版本多不说,一般都没法直接使用,最后还不得不花大价钱购买专用库。而本文针对这个问题,涉法通过python来绕过MarkEzd.dll文件,直接进行自动化控制EzCad来实现联动,可应用于实际生产。文章最后拓展一下以物联网的形式,实现手机平台等登录网页,输入想要的印字内容后,远程打印的案例。

lylelo的博客 3365

JDK源码解析之ThreadLocal类

本文通过对ThreadLocal源码解析来理解ThreaLocal

记录技术成长之路 6622

redhawk:Low Power Analysis

1.rush current与switch cell 在standby状态下为了控制leakage power我们选择power gating的设计方式,使用power switch cell关闭block/power domain的电源。 power switch的基本介绍可见: 低功耗设计-Power Switch power switch的table中有四种状态,对应block工作、待机和上电,下电。low power analysis主要分析上电的过程。 上电过程可以看做mos电容

m0_61544122的博客 3206

深入解析ThreadLocal类

先了解一下ThreadLocal类提供的几个方法: public T get() { } public void set(T value) { } public void remove() { } protected T initialValue() { }   get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,re...

weixin_30652879的博客 102

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

深入理解ThreadLocal类+Demo详细解析

通俗的来讲,ThreadLocal主要作用就是让各自线程自能取得各自线程里面的东西,也即是各自线程只能忙活自己的事,管不了别人的事,通过下面的Demo来看一下ThreadLocal是否能实现这个功能 ThreadLocal类就像Map,List这种类一样也是用来存储数据的,只是ThreadLocal类主要是为了线程数据存储而生的,目的是保证每一个线程只能存储和取出自己的东西。另外这个类是带...

yaoyaoyao_123的博客 1944

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ThreadLocal类源码解析

一、什么是ThreadLocal  该类提供了线程局部 (thread-local) 变量。这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。 例如,以下类生成对每个线程唯一的局部标识符。线程 ID

JimGray的博客 409

ThreadLocal类及其常用方法解析

背景 ThreadLocal提供了一种解决并发问题的新思路,以往为了多线程访问全局变量(共享变量)不出现安全问题,我们可以通过同步监视器来实现,而ThreadLocal是把共享变量变为线程独自拥有的变量,这样就不存在共享变量了,自然也不会有并发共享变量的问题。 栗子 先整一个栗子,方便下面对源码的理解 import java.util.Random; public class Dem...

weixin_37711738的博客 836

[java] ThreadLocal类解析

概述 该类的作用是提供线程局部(thread-local)的变量, 这些变量与正常变量的不同之处在于(通过其get或set方法)访问一个线程的每个线程都有自己的独立初始化的变量副本。ThreadLocal实例通常是希望存储状态与线程(例如,用户ID或事务ID)关联的私有静态字段。 例如,下面的类生成每个线程本地的唯一标识符。第一次调用ThreadId.get方法时,分配一个线程的ID,在后续调...

topdeveloperr的博客 340

ThreadLocal类的实例解析以及常用方法通过具体案例了解ThreadLocal的基本使用

前言小伙伴们大家好,谈到多线程就不可不谈解决线程安全 的操作类ThreadLocal了一、ThreadLocal 分析ThreadLocal会为每个线程分配独立的线程副本,解决并发访问冲突的问题,并且实现线程内的资源共享。举个例子,刚开始学Java的时候都少不了手动连接数据库吧,创建连接,填写用户名密码等(霍 死去的物种连接数据库库方式开始攻击我)

产品经理克星(狗头) 2357

threadlocal使用_ThreadLocal类使用详解及原理解析

在阅读源代码或多线程应用中,我们经常会看到ThreadLocal类,本文带你了解ThreadLocal。ThreadLocal简介在多线程的情况下,一个共享变量可能被多个线程修改,导致数据的不安全性,ThreadLocal就是为了解决这种问题,为每个线程提供该线程专属的本地变量。我们可以简单理解成,往ThreadLocal放入一个变量,ThreadLocal会为每个线程都准备一个副本变量。这样就避...

weixin_39840729的博客 159

ThreadLocal类的用法与源码解析

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操...

diexi5893的博客 133

ThreadLocal 类 的源码解析以及使用原理

1、原理图说明      首先看这一张图,我们可以看出,每一个Thread类中都存在一个属性 ThreadLocalMap 成员,该成员是一个map数据结构,map中是一个Entry的数组,存在entry实体,该实体包含了 key value hash(注意 此map结构不包含next引用 所以不是使用的链地址方法)。 可以是用来存放 ThreadLocal对象以及对应的变量副本; 根...

weixin_30549657的博客 123
上一篇: java I/O之File类:遍历目录树
下一篇: 记一次spring5源码完整编译过程
coder_no
博客等级 码龄9年 1粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值