ThreadLocal与InheritableThreadLocal学习笔记

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

首先说明,现在我坚定的认为这两个东西并不是像有的博主说的是用来解决 “共享变量的访问冲突”的。那么他们是用来干嘛的呢?顾名思义他们都和当前线程的本地变量有关。

1、ThreadLocal

a)先来看一段代码:

package net.csdn.blog;
import static java.lang.Thread.sleep;
/**
 * Created by nostalie.zhang on 2016/12/22.
 * 实现Runnable接口用以创建线程
 */
class TestClient5 implements Runnable {
    private int i = 0;
    public void run() {
        while (true) {
        //这里操作了共享数据i
                if (i < 10) {
                    System.out.println(Thread.currentThread().getName() + "---" + i);
                    i++;
                }else{
                    break;
                }

        //为了让效果更加明显使用了sleep();
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class TestThreadLocal5 {

    public static void main(String[] args) {
        TestClient5 testClient5= new TestClient5();
        //创建了两个线程
        Thread thread1 = new Thread(testClient5);
        Thread thread2 = new Thread(testClient5);
        //开启线程
        thread1.start();
        thread2.start();
    }
}

运行结果:数据出现了重复,这是并发线程访问共享数据带来的问题

Thread-1---0
Thread-0---1
Thread-1---2
Thread-0---3
Thread-1---4
Thread-0---4
Thread-1---6
Thread-0---6
Thread-0---8
Thread-1---8

Process finished with exit code 0

b)为了解决这个问题我们可以使用同步代码块如下:

package com.qunar.flight;

import static java.lang.Thread.sleep;

/**
 * Created by nostalie.zhang on 2016/12/22.
 */
class TestClient2 implements Runnable {

    private int i = 0;

    public void run() {
        while (true) {
        //同步代码块;jdk5以后也可以用lock来实现
            synchronized (this) {
                if (i < 10) {
                    System.out.println(Thread.currentThread().getName() + "---" + i);
                    i++;
                }else{
                    break;
                }
            }

            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class TestThreadLocal2 {

    public static void main(String[] args) {
        TestClient2 testClient2 = new TestClient2();
        Thread thread1 = new Thread(testClient2);
        Thread thread2 = new Thread(testClient2);
        thread1.start();
        thread2.start();
    }
}

运行结果:数据正常

Thread-0---0
Thread-1---1
Thread-0---2
Thread-1---3
Thread-1---4
Thread-0---5
Thread-1---6
Thread-0---7
Thread-0---8
Thread-1---9

Process finished with exit code 0

c)来看看使用ThreadLocal的效果

package com.qunar.flight;

/**
 * Created by nostalie.zhang on 2016/12/22.
 */
class TestClient implements Runnable {

    private  int i=0;
    //创建ThreadLocal实例
    public static ThreadLocal<Integer> ThreadID = new ThreadLocal<Integer>();

    public void run() {
        //保存i到ThreadLocal实例中
        ThreadID.set(i);
        while(ThreadID.get()<10) {
        //打印保存的i
            System.out.println(Thread.currentThread().getName() + "---" + ThreadID.get());
            //保存i+1
            ThreadID.set(ThreadID.get()+1);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class TestThreadLocal {
    public static void main(String[] args) {
        TestClient testClient=new TestClient();
        Thread thread1=new Thread(testClient);
        Thread thread2=new Thread(testClient);
        thread1.start();
        thread2.start();
        while(thread1.isAlive()||thread2.isAlive()){}
        System.out.println("testClient的变量i"+"---"+testClient.getI());
    }
}

运行结果:可以看出每一个线程都打印了从0到9。看起来就像每一个线程都把共享变量初值复制了一份保存在自己的线程空间中,而以后的操作都只针对自己保存的变量,而不去操作共享变量。注意我说的是好像

Thread-1---0
Thread-0---0
Thread-1---1
Thread-0---1
Thread-1---2
Thread-0---2
Thread-1---3
Thread-0---3
Thread-1---4
Thread-0---4
Thread-1---5
Thread-0---5
Thread-1---6
Thread-0---6
Thread-1---7
Thread-0---7
Thread-1---8
Thread-0---8
Thread-1---9
Thread-0---9
testClient的变量i---0

Process finished with exit code 0

d)继续来看这段代码:

package com.qunar.flight;

/**
 * Created by nostalie.zhang on 2016/12/22.
 */
class MyInteger4 {
    private int i = 0;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }
}

class TestClient4 implements Runnable {

    private MyInteger4 i = new MyInteger4();

    public static ThreadLocal<MyInteger4> ThreadID = new ThreadLocal<MyInteger4>();

    public void run() {
    //依旧使用了ThreadLocal,但传入的不在是基础类型而是引用类型。
        ThreadID.set(i);
        while (true) {
            if (ThreadID.get().getI() < 10) {
                System.out.println(Thread.currentThread().getName() + "---" + ThreadID.get().getI());
                if(Thread.currentThread().getName().equals("t1")){
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                MyInteger4 myInteger = ThreadID.get();
                myInteger.setI(myInteger.getI() + 1);
                ThreadID.set(myInteger);
            } else {
                break;
            }


        }
    }
}

public class TestThreadLocal4 {
    public static void main(String[] args) {
        TestClient4 testClient4 = new TestClient4();
        Thread thread1 = new Thread(testClient4);
        Thread thread2 = new Thread(testClient4);
        thread1.setName("t1");
        thread1.setPriority(10);
        thread2.setName("t2");
        thread1.start();
        thread2.start();
    }
}

运行结果如下:从这里我们可以看出,如果ThreadLocal传入的是引用类型,内部保存的只是对堆区实体的引用,而不是将堆区的对象实体复制一份。并且ThreadLocal无法保证线程安全。

t1---0
t2---0
t2---1
t2---2
t2---3
t2---4
t2---5
t2---6
t2---7
t2---8
t2---9

Process finished with exit code 0

e)我们去看一下ThreadLocal的源码实现
-ThreadLocal有一个内部类ThreadLocalMap,而Thread维护了一个ThreadLocalMap的引用。
-看一下ThreadLocal的set()方法:

 public void set(T value) {
     //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程中的ThreadLocalMap的引用
        ThreadLocalMap map = getMap(t);
        if (map != null)
        //如果不为空则将ThreadLocal作为key,传入参数作为value保存到ThreadLocalMap的引用中
            map.set(this, value);
        else
        //第一次set()引用为null,则初始化一个map并保存值,将map的引用交给Thread。
            createMap(t, value);
    }

所以真实情况就是这个样子每一个线程都有一个这样的Map<ThreadLocal,T> mapThreadLocal.ThreadLocalMap threadLocals = null;其实源码是这样的,只是为了方便说明写成这样)当你调用ThreadLocal的get()方法时就是以当前的ThreadLocal为key查找对用的value。(实现可以去看源码),set()就是以当前ThreadLocal为Key,参数为value保存到map中。
-所以一个ThreadLocal实例只是一个具体线程中的本地变量的一个索引。应用到web程序中,可以实现变量在DAO层,service层,Controller层的传递,并且这种传递在线程之间是隔绝的。也正是因为这种隔绝让很多人以为ThreadLocal解决了共享变量的冲突问题。
-ThreadLocal实现了线程内部的单例

2、InheritableThreadLocal(顾名思义:可继承的线程本地变量)

它与ThreadLocal非常相似,只不过InheritableThreadLocal的变量可以从父线程传递给子线程
看代码:

package com.qunar.flight.threadLocal;

/**
 * Created by nostalie.zhang on 2016/12/22.
 */
//使用了Junit的测试类
import junit.framework.TestCase;
import static java.lang.System.out;

public class InheritableThreadLocalTest2 extends TestCase {

    public void testThreadLocal() {
        final ThreadLocal<StringBuilder> local = new ThreadLocal<StringBuilder>();
        work(local);
    }

    public void testInheritableThreadLocal() {
        final ThreadLocal<StringBuilder> local = new InheritableThreadLocal<StringBuilder>();
        work(local);
    }

    private void work(final ThreadLocal<StringBuilder> local) {
        local.set(new StringBuilder("a"));
        out.println(Thread.currentThread() + "," + local.get());
        Thread t = new Thread(new Runnable() {

            public void run() {
                out.println(Thread.currentThread() + "," + local.get());
                local.set(new StringBuilder("b"));
                out.println(Thread.currentThread() + "," + local.get());
            }
        });

        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        out.println(Thread.currentThread() + "," + local.get());
    }
}

两个测试结果如下:不做过多说明
testThreadLocal()

Thread[main,5,main],a
Thread[Thread-0,5,main],null
Thread[Thread-0,5,main],b
Thread[main,5,main],a

Process finished with exit code 0

testInheritableThreadLocal()

Thread[main,5,main],a
Thread[Thread-0,5,main],a
Thread[Thread-0,5,main],b
Thread[main,5,main],a

Process finished with exit code 0

表述能力有限,说的不是很清楚,推荐博客(其它的真心没有看出诚意):
http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/

ThreadLocalInheritableThreadLocal、TransmittableThreadLocal 线程访问同一个共享变量的时候容易出现并发问题,ThreadLocal是除了加锁这种同步方式之外的一种保证规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。 使用ThreadLocal的最终目的还是为了得到安全的数据,下面分析下ThreadLocal的结构 一、在提到ThreadLocal前我们先看下Thread类以及属性 class T 阅读详情

相关推荐

java中ThreadLocalInheritableThreadLocal详解

一、ThreadLocal介绍 ​ 在多线程环境下访问同一个线程的时候会出现并发问题,特别是多个线程同时对一个变量进行写入操作时,为了保证线程的安全,通常会进行加锁来保证线程的安全,但是加锁又会造成效率的降低;ThreadLocal是jdk提供的除了加锁之外保证线程安全的方法,其实现原理是在Thread类中定义了两个ThreadLocalMap类型变量threadLocals、inheritableThreadLocals用来存储当前操作的ThreadLocal的引用及变量对象,这样就可以把当前线程的变

OceanSky的专栏 5533

ThreadLocalnheritableThreadLocal的区别及使用

线程环境中,共享变量的并发修改常常导致线程同步问题,ThreadLocal可以存储线程私有的本地变量,从而使线程之间的变量相互隔离因为ThreadLocal线程执行的上下文可以传递变量的特性,所以可以很好的解决变量值传递问题:常见的web项目中,session需要在执行过程中进行传递,此时我们有两种选择每一个需要调用的方法,都增加session的入参使用ThreadLocal

JavaSupeMan的博客 1883

【win11 cuda12.0安装tensorflow-gpu】

win11 cuda 12.0 安装 tensorflow-gpu

weixin_36299320的博客 2979

ThreadLocal/InheritableThreadLocal/TransmittableThreadLocal 应用场景以及核心源码解析

ThreadLocal的应用非常广泛,就ThreadLocal可以实现当前线程的共享数据获取这个就太有用了。正常我们写代码都是由上一个方法传递个下一个方法来进行传递参数,假如说参数繁多,链路非常长,这个时候就会导致形参非常臃肿、传递参数非常不易。这个时候就可以使用ThreadLocal来解决这个问题。

yh4494的博客 1012

ThreadLocalInheritableThreadLocal 和TransmittableThreadLocal 的区别

描述 用途 ThreadLocal 线程本地变量 线程本地存储 InheritableThreadLocal 可继承的线程本地变量 线程本地存储、线程间共享 TransmittableThreadLocal (阿里开源的,需要导包) 可传递的线程本地变量 线程本地存储、线程间共享、线程池共享 注:其存储功能体现在Thread,ThreadLocal只能操作本地数据,底层是通过ThreadLocalMap实现的,而ThreadLocalMap类通过key为ThreadLocal类...

weixin_45789266的博客 643

ThreadLocalInheritableThreadLocal学习笔记

ThreadLocal为多线程并发问题提供了一种新的解决思路,因为它可以单独给每一个线程提供独立的变量副本,这样每一个线程就可以只修改自己的副本变量,互不影响。 场景1: 每一个线程可以保存一个独立副本,线程A的修改,不影响线程B;比如SimpleDateFormat在多并发场景下就不能使用,可以使用ThreadLocal解决这个问题; 场景2: 数据库主从分离,比如某一个查询服务,这个过程需要查询多张表,涉及到多个DAO层,我们可以在SERVICE层指定使用主库或者存库,然后整个过程就只使用主库、或者只

燕少江湖 298

Java 学习笔记ThreadLocalInheritableThreadLocal、TransmittableThreadLocal

一、初识 服务程序是由进程构成,进程是由无数个线程构成,线程是一组代码片段组成。在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。 需要注意的是: 若 ThreadLocal 存储的变量是引用变量,可以对所有线程中的threadLocal 通过 set() 方法 设置

qq_38276913的博客 339

第3章 Spring Security 认证Authentication

在Spring Security中 Authentication 对象代表认证用户的数据对象,包含一些用户基本信息(用户名、密码)。 Authentication接口 public interface Authentication extends Principal, Serializable { // 获取用户权限 Collection<? extends GrandtedAuthoriry> getAuthorities(); // 获取用户凭证,一般就是密码

Shiro框架02 658

ThreadLocalInheritableThreadLocal

ThreadLocalInheritableThreadLocal

正在努力的小小小白 992

ThreadLocalInheritableThreadLocal、TransmittableThreadLocal三者之间区别

本文主要介绍ThreadLocalInheritableThreadLocal、TransmittableThreadLocal三者之间区别、如何使用、什么场景使用以及对原理和源码的介绍。介绍原理的时候通过最直白、最易懂的语言争取让大家了解三者之间的区别,以及日常如何把他们使用起来 ThreadLocal ThreadLocal解决的是每个线程可以拥有自己线程的变量实例。可以从隔离的角度解决变量线程安全的问题。 举个例子: 用户登陆后将用户的信息保存到ThreadLocal中,ThreadLocal .

weixin_43954303的博客 6739

ThreadLocalInheritableThreadLocal详解

ThreadLocalInheritableThreadLocal详解 多线程访问同一个共享变量时,容易出现并发冲突,为了保证线程的安全,一般使用者在访问共享变量时,需要进行适量的同步。而ThreadLocal提供了线程的私有变量,每个线程都可以通过set()和get()来对这个私有变量进行操作,但不会和其他线程的私有变量进行冲突,实现了线程的数据隔离。InheritableThreadLoca...

zycxnanwang的博客 1504

ThreadLocalInheritableThreadLocal、TransmittableThreadLocal原理及Demo

ThreadLocal可以通过key.get()==null来判断Key是否已经被回收,如果Key被回收,就说明当前Entry是一个废弃的过期节点,ThreadLocal会自发的将其清理掉。由于线程池复用了已有线程,所以没有调用init()方法这个过程,也就不能将父线程中的InheritableThreadLocal值传给子线程。本身就是清理当前节点的,但是为了避免发生「内存泄漏」,ThreadLocal还会检查容器中是否还有其他过期节点,如果发现也会一并清理,主要逻辑在。

m0_45164511的博客 1956

ThreadLocalInheritableThreadLocal、TransmittableThreadLocal解析

文章目录使用场景ThreadLocalDemoThreadLocal存储结构关键APIset方法实现get方法实现缺点内存泄露父子线程传值问题InheritableThreadLocal线程上下文复制Demo缺点TransmittableThreadLocalDemo原理TransmittableThreadLocal#holderTransmittableThreadLocal#set捕获重放...

hosaos的博客 1955

ThreadLocalInheritableThreadLocal的实现原理

的特点就是线程绑定,一般通过这种隐式传参的方式来传递上下文。比如,用户登录和获取用户相关信息,这时候如果在每个需要用户信息的方法入参上加入用户信息参数就先的非常冗余,不够优雅。还有就是日志的链路信息等等。根据上面的问题我们知道存在的局限性,也就是可继承的ThreadLocal,这里的可继承就是指的。它实现可继承的方式,就如前文描述的一样,线程池使用 ThreadLocal InheritableThreadLocal 取值为null

唐芬奇的博客 1576

ThreadLocal,InheritableThreadLocal 使用及原理

理解ThreadLocal的原理,使用,破除一些ThreadLocal的迷信

jike920203的博客 1425

ThreadLocalInheritableThreadLocal,TransmittableThreadLocal

文章目录1 ThreadLocal1.1 基本理解1.2 应用1.2.1 使用场景1.2.2 在 Spring 中的使用->解决线程安全问题1.3 原理1.3.1 ThreadLocal的结构1.3.2 ThreadLocal 的方法1.3.3 ThreadLocal的内部类ThreadLocalMap1.4 总结 1 ThreadLocal 1.1 基本理解 ThreadLocal 是......

上善若泪 2354

ThreadLocalInheritableThreadLocal

一、引子 最近在研究fps帧率时候了解到可以通过Choreographer刷新帧率的方法来统计界面刷新帧率,在当前应用确实可以,但是却不能监测其他app(比如QQ、微信、酷我音乐),为什么呢?难道说Choreographer不是全局变量。 ...

努力的八戒 1333

ThreadLocal分析五:InheritableThreadLocal

通过上面几篇文章的分析,大家已经了解了threadlocal的数据结构,以及源码分析。但是他有什么缺点呢? 答案是threadlocal不能在父子线程线程之间传递 public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args) {

Chen_leilei的博客 4168

ThreadLocalInheritableThreadLocal

目录 一 ThreadLocal 是什么 二 ThreadLocal 实现原理 三 ThreadLocal 应用场景 四 参考文档 一 ThreadLocal 是什么 ThreadLocal 本地线程变量,通过 set() 和 get() 方法来维护局部变量。不同线程之间局部变量彼此隔离,互不影响。 二 ThreadLocal 实现原理 2.1 我们可以先来看看 Th...

javaTalk 611

ThreadLocalInheritableThreadLocal 再到 TransmittableThreadLocal

ThreadLocal 是 Java 提供的一个线程级别的变量存储类,它允许你创建只能被同一个线程读写的变量,即使多个线程同时访问同一个 ThreadLocal 对象,每个线程也只能看到自己线程的变量副本。

qq_34368390的博客 735
上一篇: 随笔
下一篇: java枚举学习笔记
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值