我们先看以下代码,不用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的简单使用和实现原理的更多相关文章

  1. 理解Promise简单实现的背后原理

    在写javascript时我们往往离不开异步操作,过去我们往往通过回调函数多层嵌套来解决后一个异步操作依赖前一个异步操作,然后为了解决回调地域的痛点,出现了一些解决方案比如事件订阅/发布的.事件监听的 ...

  2. 面试官: Flink双流JOIN了解吗? 简单说说其实现原理

    摘要:今天和大家聊聊Flink双流Join问题.这是一个高频面试点,也是工作中常遇到的一种真实场景. 本文分享自华为云社区<万字直通面试:Flink双流JOIN>,作者:大数据兵工厂 . ...

  3. ThreadLocal的设计与使用(原理篇)

    在jdk1.2推出时开始支持java.lang.ThreadLocal.在J2SE5.0中的声明为:            public class ThreadLocal<T> exte ...

  4. Java并发:ThreadLocal的简单介绍

    作者:汤圆 个人博客:javalover.cc 前言 前面在线程的安全性中介绍过全局变量(成员变量)和局部变量(方法或代码块内的变量),前者在多线程中是不安全的,需要加锁等机制来确保安全,后者是线程安 ...

  5. 菜鸟学SSH(十五)——简单模拟Hibernate实现原理

    之前写了Spring的实现原理,今天我们接着聊聊Hibernate的实现原理,这篇文章只是简单的模拟一下Hibernate的原理,主要是模拟了一下Hibernate的Session类.好了,废话不多说 ...

  6. JAVA线程及简单同步实现的原理解析

    线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...

  7. mybatis学习系列--逆向工程简单使用及mybatis原理

    2逆向工程简单测试(68-70) SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession session = sq ...

  8. ThreadLocal的使用场景及实现原理

    1. 什么是ThreadLocal? 线程局部变量(通常,ThreadLocal变量是private static修饰的,此时ThreadLocal变量相当于成为了线程内部的全局变量) 2. 使用场景 ...

  9. 简单透彻理解JSONP原理及使用

    首先提一下JSON这个概念,JSON是一种轻量级的数据传输格式,被广泛应用于当前Web应用中.JSON格式数据的编码和解析基本在所有主流语言中都被实现,所以现在大部分前后端分离的架构都以JSON格式进 ...

随机推荐

  1. 利用Grafana展示zabbix数据

    一.系统搭建(以Centos7为例)因为我们的主要目的是展示zabbix的数据,所以建议大家直接在zabbix的服务器上搭建这个系统,亲测两系统无冲突,这样部署的好处是两系统间的数据传输更快,前端展示 ...

  2. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  3. RestTemplate的逆袭之路,从发送请求到负载均衡

    上篇文章我们详细的介绍了RestTemplate发送请求的问题,熟悉Spring的小伙伴可能会发现:RestTemplate不就是Spring提供的一个发送请求的工具吗?它什么时候具有了实现客户端负载 ...

  4. [Python Web]常见的 POST 提交数据的方式

    本文参考整理于:https://imququ.com/post/four-ways-to-post-data-in-http.html 简介 这里介绍了,用 POST 方法提交数据时,常见的三种方式: ...

  5. Spring Boot2.0 设置拦截器

    所有功能完成 配置登录认证 配置拦截器 在spring boot2.0 之后 通过继承这个WebMvcConfigurer类 就可以完成拦截 新建包com.example.interceptor; 创 ...

  6. 实现一个简单的WebSocket聊天室

    WebSocket 简介 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主 ...

  7. DSAPI HTTP监听服务端与客户端

    本文中,演示了使用DSAPI.网络相关.HTTP监听,快速建立服务端和客户端. HTTP监听服务端的作用,是监听指定计算机端口,以实现与IIS相同的解析服务,提供客户端的网页请求,当然,这不仅仅是应用 ...

  8. tomcat 大并发报错 Maximum number of threads (200) created for connector with address null and port 80

    1.INFO: Maximum number of threads (200) created for connector with address null and port 80 说明:最大线程数 ...

  9. 委托(3).net 2.0中的委托

    由于.net 2.0引入了匿名方法,所以创建委托的方式可以更简化了. .net 2.0中的委托 有了匿名方法,上一篇的例子可以简化为: namespace DelegateDemo { //声明委托 ...

  10. vs2013中集成Git

    一:为什么不用2013中自带的? 我的2013自带的没法用,连最基本的克隆都用不了,网上看着下  好像说都不能用:不知道各位的如何. 二:如何自己讲Git集成到vs2013中? 需要的工具:     ...