ThreadLocal的简单使用和实现原理
我们先看以下代码,不用ThreadLocal会发生什么情况
package com.qjc.thread.threadLocal; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; //ThreadLocal顾名思义表示线程的局部变量,及只有当前线程可以访问,自然是线程安全的
public class ThreadLocalTest { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static class ParseDate implements Runnable {
int i = 0; public ParseDate(int i) {
this.i = i;
} @Override
public void run() {
try {
Date parse = sdf.parse("2018-04-19 15:12:" + i % 60);
System.out.println(i + ":" + parse);
} catch (ParseException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new ParseDate(i));
}
executorService.shutdown();
}
}
控制台输出了这么一个异常

很明显是异常出现在线程上,表明这样做是线程不安全的
下面,我们用ThreadLocal,代码如下:
package com.qjc.thread.threadLocal; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; //ThreadLocal顾名思义表示线程的局部变量,及只有当前线程可以访问,自然是线程安全的
public class ThreadLocalTest { // private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<>(); public static class ParseDate implements Runnable {
int i = 0; public ParseDate(int i) {
this.i = i;
} @Override
public void run() {
try {
// 用ThreadLocal
if (threadLocal.get() == null) {
threadLocal.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
Date parse = threadLocal.get().parse("2018-04-19 15:12:" + i % 60);
// 不用ThreadLocal
// Date parse = sdf.parse("2018-04-19 15:12:" + i % 60);
System.out.println(i + ":" + parse);
} catch (ParseException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executorService.execute(new ParseDate(i));
}
executorService.shutdown();
}
}
控制台,正常输出,没有异常出现,表明线程安全。
我们分析一下ThreadLocal的实现原理:
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程对象
ThreadLocalMap map = getMap(t);//拿到线程的ThreadLocalMap,它是Thread内部的成员(Thread类中的):ThreadLocal.ThreadLocalMap threadLocals = null;
//并将值设入ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);//从此可以看出,ThreadLocalMap的key就是ThreadLocal当前对象,value就是我们设置的值
}
public T get() {
Thread t = Thread.currentThread();//获取当前线程对象
ThreadLocalMap map = getMap(t);//获取当前线程的ThreadLocalMap对象
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//然后通过将自己作为key取得内部的实际数据
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal的简单使用和实现原理的更多相关文章
- 理解Promise简单实现的背后原理
在写javascript时我们往往离不开异步操作,过去我们往往通过回调函数多层嵌套来解决后一个异步操作依赖前一个异步操作,然后为了解决回调地域的痛点,出现了一些解决方案比如事件订阅/发布的.事件监听的 ...
- 面试官: Flink双流JOIN了解吗? 简单说说其实现原理
摘要:今天和大家聊聊Flink双流Join问题.这是一个高频面试点,也是工作中常遇到的一种真实场景. 本文分享自华为云社区<万字直通面试:Flink双流JOIN>,作者:大数据兵工厂 . ...
- ThreadLocal的设计与使用(原理篇)
在jdk1.2推出时开始支持java.lang.ThreadLocal.在J2SE5.0中的声明为: public class ThreadLocal<T> exte ...
- Java并发:ThreadLocal的简单介绍
作者:汤圆 个人博客:javalover.cc 前言 前面在线程的安全性中介绍过全局变量(成员变量)和局部变量(方法或代码块内的变量),前者在多线程中是不安全的,需要加锁等机制来确保安全,后者是线程安 ...
- 菜鸟学SSH(十五)——简单模拟Hibernate实现原理
之前写了Spring的实现原理,今天我们接着聊聊Hibernate的实现原理,这篇文章只是简单的模拟一下Hibernate的原理,主要是模拟了一下Hibernate的Session类.好了,废话不多说 ...
- JAVA线程及简单同步实现的原理解析
线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...
- mybatis学习系列--逆向工程简单使用及mybatis原理
2逆向工程简单测试(68-70) SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession session = sq ...
- ThreadLocal的使用场景及实现原理
1. 什么是ThreadLocal? 线程局部变量(通常,ThreadLocal变量是private static修饰的,此时ThreadLocal变量相当于成为了线程内部的全局变量) 2. 使用场景 ...
- 简单透彻理解JSONP原理及使用
首先提一下JSON这个概念,JSON是一种轻量级的数据传输格式,被广泛应用于当前Web应用中.JSON格式数据的编码和解析基本在所有主流语言中都被实现,所以现在大部分前后端分离的架构都以JSON格式进 ...
随机推荐
- firewall防火墙
firewall防火墙 1 防火墙简介 在基于RHEL7的服务器,提供了一个firewall的动态管理的防火墙,其支持IPv4和IPv6,还支持以太网桥,并有分离运行时间和永久性配置选择.它还具备一个 ...
- asp.net core系列 34 EF保存数据(1)
一. 基本数据 每个EF上下文实例都有一个 ChangeTracker(更改跟踪器),它负责跟踪需要写入数据库的更改. 当更改实体类的实例时(修改属性,删除实例,新建实例等),这些更改会记录在 Cha ...
- redis 系列23 哨兵Sentinel (上)
一.概述 Sentinel(哨岗或哨兵)是Redis的高可用解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主 ...
- Hive 导入 parquet 格式数据
Hive 导入 parquet 数据步骤如下: 查看 parquet 文件的格式 构造建表语句 倒入数据 一.查看 parquet 内容和结构 下载地址 社区工具 GitHub 地址 命令 查看结构: ...
- Connection open error . Connection Timeout Expired. The timeout period elapsed during the post-login phase.
是这样的,最近我在开发Api(重构),用的数据库是Sqlserver,使用的Orm是 SqlSugar(别问我为什么选这个,boss选的同时我也想支持国人写的东西,且文档也很全). 被催的是,写好了程 ...
- git版本控制工具的使用
目录 git版本管理工具使用 一丶Git的下载与安装 1.windows下的git的下载与安装 2.linux下的git安装 二丶常用命令 三丶Git仓库 1.配置仓库信息 2.仓库的创建于管理 四丶 ...
- 做了2个多月的设计和编码,我梳理了Flutter动态化的方案对比及最佳实现
背景 在端上为了提升App的灵活性, 快速解决万变的业务需求,开发者们探索了多种解决方案,如PhoneGap ,React Native ,Weex等,但在Flutter生态还没有好的解决方案.未来闲 ...
- MySQL备份与恢复之percona-xtrabackup实现增量备份及恢复 实例
innobackupex 的使用方法1.完全备份 参数一是完全备份地址 完全备份到/data/mysql/back_up/all_testdb_20140612 目录下innobackupex --u ...
- springmvc 项目完整示例03 小结
利用spring 创建一个web项目 大致原理 利用spring的ioc 原理,例子中也就是体现在了配置文件中 设置了自动扫描注解 配置了数据库信息等 一般一个项目,主要有domain,dao,ser ...
- oracle账户登录数据库进行如下操作:
CREATE USER NORTHBOUND IDENTIFIED BY NORTHBOUND DEFAULT TABLESPACE "TBS_DNINMSV31" TEMPORA ...