ThreadLocal使用的情景和原理

线程本地存储,ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。这是防止多线程在资源上产生冲突的第二种方式,即每个线程都有一个完整的副本,

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

理解threadLocal和thread从下面两个点出发,很清晰:
先看获取某个threadLocal值过程:
获取当前线程及其threadLocalMap  -- 某threadLocal同时用到多个多个thread
threadlocalMap中以threadLocal为key获取value -- 某线程中有多个threadLocal


1 threadLocal被多个thread调用,怎么区分多个thread呢?
每个thread中有一个thread.threadLocalMap,threadLocal被某个thread调用时会获取该thread的threadLocalMap

2 某个thread可以使用多个threadLocal,所以为了区分这些threadLocal,threadLocalMap结构为:
key-threadLocal
value-object

当多个线程需要使用同一个对象,并且需要该对象具有相同的初始化值 的情景下使用ThreadLocal

实例:

package concurrencypractice.jingtong.review;

 

public class MythreadExtendsThread extends Thread{

private sequenceB sequence;

public MythreadExtendsThread(sequenceB sequence){

this.sequence=sequence;

}

public void run(){

// TODO Auto-generated method stub

for(int i=0;i<3;i++){

System.out.println(Thread.currentThread().getName()+" i="+i+"sequence.getNumber()"+sequence.getNumber());

try {

Thread.sleep(i*1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

 

 

package concurrencypractice.jingtong.review;

 

public class sequenceB {

 

private static ThreadLocal<Integer> numberContainer=new ThreadLocal<Integer>()/*{

//threadLocal赋初值的方法一

protected Integer initialValue(){

return 0;

}

}*/;

public int getNumber(){

//threadLocal赋初值方法二

if(numberContainer.get()==null){

numberContainer.set(0);

}else{

numberContainer.set(numberContainer.get()+1);

 

}

return (int) numberContainer.get();

}

public static void main(String[] args) {

sequenceB sequnce=new sequenceB();

MythreadExtendsThread thread=new MythreadExtendsThread(sequnce);

MythreadExtendsThread thread1=new MythreadExtendsThread(sequnce);

MythreadExtendsThread thread2=new MythreadExtendsThread(sequnce);

thread.start();

thread1.start();

thread2.start();

}

}

打印结果

Thread-1 i=0sequence.getNumber()0

Thread-2 i=0sequence.getNumber()0

Thread-0 i=0sequence.getNumber()0

Thread-2 i=1sequence.getNumber()1

Thread-1 i=1sequence.getNumber()1

Thread-0 i=1sequence.getNumber()1

Thread-1 i=2sequence.getNumber()2

Thread-2 i=2sequence.getNumber()2

Thread-0 i=2sequence.getNumber()2

 

ThreadLocal的底层原理分析

本质上是每个线程内部有一个ThreadLocalMap,所以获取threadLocalmap需要先获取当前线程;该threadLocalMap中key为threadLocal自身,value为真实的值。

提供的方法: 先了解一下ThreadLocal类提供的几个方法:

1

2

3

4

public T get() { }

public void set(T value) { }

public void remove() { }

protected T initialValue() { }

  get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。

 

先看下get方法的实现:

 

   第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是 this就是ThreadLocal对象,而不是当前线程t。

  如果获取成功,则返回value值。

  如果map为空,则调用setInitialValue方法返回value。

  我们上面的每一句来仔细分析:

  首先看一下getMap方法中做了什么:

 

  可能大家没有想到的是,在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。

  那么我们继续取Thread类中取看一下成员变量threadLocals是什么:

 

  实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,我们继续取看ThreadLocalMap的实现:

  可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。

  然后再继续看setInitialValue方法的具体实现:

 

  很容易了解,就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现:

 

  至此,可能大部分朋友已经明白了ThreadLocal是如何为每个线程创建变量的副本的:

  首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。

  初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

 

1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的numberContainer

3)在进行get之前,必须先set如果没有set则会调用setInitialValue初始化threadLocals,否则会返回null

理解threadLocal和thread从下面两个点出发,很清晰:
1 threadLocal被多个thread调用,怎么区分多个thread呢?
每个thread中有一个thread.threadLocalMap,threadLocal被某个thread调用时会获取该thread的threadLocalMap

2 某个thread可以使用多个threadLocal,所以为了区分这些threadLocal,threadLocalMap结构为:
key-threadLocal
value-object

借鉴:

https://www.cnblogs.com/dolphin0520/p/3920407.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值