ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。

举一个反面例子,当我们使用简单的int类型存储线程间共享的数据,但在另外一个线程我们想共享另外一份数据,此时就会造成数据混淆的现象,如下:

package com.zzj.test;

import java.util.Random;

public class Test {

    private static int data;

    public static void main(String[] args) {
for(int i = 1; i <= 2; i ++) {
new Thread(() -> {
data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has get data: " + data);
new A().get();
new B().get();
}).start();
}
} private static class A{
public void get() {
System.out.println("A from " + Thread.currentThread().getName() + " has get data: " + data);
}
} private static class B{
public void get() {
System.out.println("B from " + Thread.currentThread().getName() + " has get data: " + data);
}
}
}

运行结果如下:

而当我们使用ThreadLocal时,就不会出现数据混淆的现象:

public class Test {

    private static ThreadLocal<Integer> tl = new ThreadLocal<>();

    public static void main(String[] args) {
for(int i = 1; i <= 2; i ++) {
new Thread(() -> {
int data = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has get data: " + data);
tl.set(data);
new A().get();
new B().get();
}).start();
}
} private static class A{
public void get() {
int data = tl.get();
System.out.println("A from " + Thread.currentThread().getName() + " has get data: " + data);
}
} private static class B{
public void get() {
int data = tl.get();
System.out.println("B from " + Thread.currentThread().getName() + " has get data: " + data);
}
}
}

结果如下:

我们找到ThreadLocal的源码,看看其基本运行原理:

    public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
} public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
} public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
} // 通过静态内部类实现变量与线程绑定
static class ThreadLocalMap {...}

ThreadLocal基本运行过程:每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key是各自的线程对象,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更快地释放内存,不调用在线程结束后也会自动释放相关的ThreadLocal变量。

结论--我们结合源码和上述两个例子可以看出:

ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
 
ThreadLocal的应用场景:把转出账户的余额减少,转入账户的余额增加,这两个操作在同一个事务中完成,它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的对象。
 
 

详述ThreadLocal的更多相关文章

  1. ThreadLocal使用和原理简析

    1. 解决共享资源冲突 对于并发工作,需要某种方式来防止两个任务同时访问相同的资源,至少在关键阶段不能出现这种冲突情况. 方法之一就是当资源被一个任务使用时,在其上加锁.第一个访问某项资源的任务必须锁 ...

  2. Android10_原理机制系列_Android消息机制(Handler)详述

    概述 在Android中的多进程.多线程中提过,只有主线程(UI线程)可以更新UI,其他线程不可以,所以一般耗时操作放到子线程.子线程可以通过Handler将相关信息通知到主线程. Android的消 ...

  3. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  4. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  5. Threadlocal使用Case

    Threadlocal能够为每个线程分配一份单独的副本,使的线程与线程之间能够独立的访问各自副本.Threadlocal 内部维护一个Map,key为线程的名字,value为对应操作的副本. /** ...

  6. 多线程映射工具——ThreadLocal

    ThreadLocal相当于一个Map<Thread, T>,各线程使用自己的线程对象Thread.currentThread()作为键存取数据,但ThreadLocal实际上是一个包装了 ...

  7. ThreadLocal 工作原理、部分源码分析

    1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap t ...

  8. ThreadLocal<T>的是否有设计问题

    一.吐槽 ThreadLocal<T>明显是.NET从JAVA中来的一个概念,但是这种设计是否出现了问题. 很明显,在JAVA中threadLocal直接是Thread的成员,当然随着th ...

  9. 理解ThreadLocal —— 一个map的key

    作用: 当工作于多线程中的对象使用ThreadLocal维护变量时,threadLocal为每个使用该变量的线程分配一个独立的变量副本. 接口方法: protected T initialValue( ...

随机推荐

  1. phpRedis函数使用总结【分类详细】

    <?php /*1.Connection*/ $redis = new Redis(); $redis->connect('127.0.0.1',6379,1);//短链接,本地host, ...

  2. 解决linux 运行自动化脚本浏览器无法启动问题

    1.前提你的驱动和版本对应无问题时,依旧报未知错误无法启动chrome 解决方法加上两行: options.addArguments("no-sandbox");options.a ...

  3. rapidxml编写xml文件(一)

    int writeXML(void) { rapidxml::xml_document<> doc; rapidxml::xml_node<> *rot = doc.alloc ...

  4. Python - int()

    参考 https://docs.python.org/3/library/functions.html?highlight=int#int If x is not a number or if bas ...

  5. 《开课》---创业学习--训练营直播第一课--HHR

    1,分享三点:敬畏心,学习过程,带着目标学习. 2,融资是个非常专业的技术活. 3,一定不要盲目自信,在资本市场,融资永远是个小概率事件. 4,学习过程:每周解锁学习资料,固定的时间来进行作业答疑. ...

  6. 【原】rsync使用

    在使用jenkins当跳板机的场景下,有使用git pull 代码到jenkins机器后,需要将代码复制到另一台机器上,常用的复制命令有scp和rsync:现就使用到了rsync进行详解: rsync ...

  7. Java IO流详解(三)——字节流InputStream和OutPutStream

    我们都知道在计算机中,无论是文本.图片.音频还是视频,所有的文件都是以二进制(字节)形式存在的,IO流中针对字节的输入输出提供了一系列的流,统称为字节流.字节流是程序中最常用的流.在JDK中,提供了两 ...

  8. 利用ProxySQL实现MySQL的读写分离

    本文简单介绍ProxySQL的安装及如果实现后端MySQL主从结构的读写分离. 一.ProxySQL安装 Proxy官方地址:https://proxysql.com/ proxysql-2.0.8- ...

  9. Nginx平滑升级版本!(重点)

    一.解释nginx的平滑升级 随着nginx越来越流行使用,并且nginx的优势也越来越明显,nginx的版本迭代也开始了加速模式,1.9.0版本的nginx更新了许多新功能,例如stream四层代理 ...

  10. idea提示非法字符

    问题: 解决方法: 将编码格式UTF-8+BOM文件转为普通的UTF-8文件. 一.简单方法,在AS右下角,将编码改为GBK,再转为UTF-8,可以解决. 二.可以用EditPlus 1.将文件用Ed ...