`
春花秋月何时了
  • 浏览: 39983 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

ThreadLocal线程本地变量

 
阅读更多

概念

ThreadLoacl主要是用来做线程间共享但又不关联的共享变量的存储。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,也就是只是共用这个变量以及该变量的初始值,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

  

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadLocalTest {

	public void test(){
        ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue() {
                return 0;
            }
        };
        ExecutorService service = Executors.newFixedThreadPool(3);
        for(int i=0;i<3;i++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                	Integer num = local.get();
                	Integer init = num;
                	while(++num<10); 
                	System.out.println("当前线程为:" + Thread.currentThread().getName() + ",初始值为:"+init+",结果数据为:" + num);
                }
            });
        }
		service.shutdown();
    }
	
	public static void main(String[] args) {
		new ThreadLocalTest().test();
	}
}

  

当前线程为:pool-1-thread-1,初始值为:0,结果数据为:10
当前线程为:pool-1-thread-3,初始值为:0,结果数据为:10
当前线程为:pool-1-thread-2,初始值为:0,结果数据为:10

 通过测试代码可以看到,虽然三个线程使用了同一个ThreadLocal变量,并各自进行加到10操作,但是三个线程之间并没有相互影响,而只是共享了变量初始值。

 

ThreadLocal源码分析

1. get()

首先查看ThreadLocal构造方法,发现什么也没做,那么查看get()方法:

 

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();
    }
 可以看到get方法首先取得当前线程对象,接着借此调用getMap方法返回ThreadLocalMap对象。
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}
 而getMap方法最终是从当前线程中取得ThreadLocalMap实例,查看线程类:
/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
 发现它是线程类的实例成员,也就是说,每一个线程都有自己独立的ThreadLocalMap实例。
在我们第一次调用ThreadLocal的get()方法时,getMap()方法返回是null(这一点可以通过查看Thread类的构造方法最终执行的init方法发现,构造线程时没有对ThreadLocal.ThreadLocalMap threadLocals进行初始化),所以执行了setInitialValue方法。
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
}
 其中initialValue()方法就是我们重写过的方法,在上面的例子中我们返回的是0.由于是第一次执行,getMap(t)方法方的是null,所以接着看createMap方法:
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
}
终于创建了当前线程t的ThreadLocalMap,其中参数this为当前ThreadLocal实例,firstValue为其初始值:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
}
 在ThreadLocalMap构造方法中,创建了一个类似Map的对象,并且以ThreadLocal实例为key,initialValue()方法的返回值为value。所以每一个线程都有一个独立的这样的ThreadLocalMap对象将ThreadLocal实例与其值关联起来。
接下来,如果第二次调用get()方法,会怎么样呢?
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();
    }
 由于getMap(t)不再返回null,所以进入if块,然后以当前ThreadLocal实例为Key,取得当前线程的ThreadLocalMap中对应的值,当然如果发现不存在这样的键值对entry,那么还是进行执行setInitialValue.
2. set(T value)
这个方法就是重新设置每一个线程的本地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);
}
 这里取得当前线程,然后根据当前的thradLocal实例取得其map。然后重新设置 map.set(this, value);这时这个线程的thradLocal里的变量副本就被重新设置值了!
3. remove()
就是清空ThreadLocalMap里的key-value键值对,这样一来。下次再调用get时又会调用initialValue这个方法返回设置的初始值.
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
}
 
总结:
1、每个线程都有自己的局部变量
每个线程都有一个独立于其他线程的上下文ThreadLocalMap来保存这个变量。
2、独立于变量的初始化副本
ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝。
3、状态与某一个线程相关联
ThreadLocal 不是用于解决共享变量的问题的,也不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制。
分享到:
评论

相关推荐

    ThreadLocal 线程本地变量 及 源码分析.rar_开发_设计

    ThreadLocal 线程微本地变量 及 源码分析

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    java线程本地变量ThreadLocal详解

    ThreadLocal则为每一个线程提供了一个变量副本,从而隔离了多个线程访问数据的冲突,ThreadLocal提供了线程安全的对象封装,下面我们就来详细了解一下吧

    Java多线程编程之ThreadLocal线程范围内的共享变量

    主要介绍了Java多线程编程之ThreadLocal线程范围内的共享变量,本文讲解了ThreadLocal的作用和目的、ThreadLocal的应用场景、ThreadLocal的使用实例等,需要的朋友可以参考下

    Hibernate用ThreadLocal模式(线程局部变量模式)管理Session

    今天小编就为大家分享一篇关于Hibernate用ThreadLocal模式(线程局部变量模式)管理Session,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

    ThreadLocal详解及说明

    关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...

    深入解析Java中ThreadLocal线程类的作用和用法

    ThreadLocal为解决多线程程序的并发问题提供了一种新的思路,ThreadLocal并不是一个Thread,而是Thread的局部变量,本文就来深入解析Java中ThreadLocal线程类的作用和用法.

    Java多线程编程中ThreadLocal类的用法及深入

    ThreadLocal,直译为“线程本地”或“本地线程”,如果你真的这么认为,那就错了!其实,它就是一个容器,用于存放线程的局部变量,我认为应该叫做 ThreadLocalVariable(线程局部变量)才对,真不理解为什么当初 ...

    谈谈Java中的ThreadLocal

     ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。  跳出误区  需要...

    对ThreadLocal的理解【源码分析+应用举例】

    ThreadLocal是JDK包提供的,它提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每一个线程都会有这个变量的一个本地副本。当多线程操作这个变量时,实际操作的就是自己本地内存里面...

    Java ThreadLocal用法实例详解

    主要介绍了Java ThreadLocal用法,结合实例形式详细分析了ThreadLocal线程局部变量相关原理、定义与使用方法,需要的朋友可以参考下

    java事务 - threadlocal

    ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量

    JAVA ThreadLocal类深入

    深入研究java.lang.ThreadLocal类。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是 threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。

    SpringBoot实现动态切换数据源(含源码)

    `ThreadLocal` 是 Java 中的一个类,用于存储线程局部变量。线程局部变量与普通的变量不同,它不是共享的,每个线程都有其自己的独立的线程局部变量副本。这使得我们可以在多线程环境中为每个线程提供独立的变量副本...

    8个案例详解教会你ThreadLocal.docx

    通常情况下,我们创建的成员变量都是线程不安全的。因为他可能被多个线程同时修改,此变量对于多...而使用ThreadLocal创建的变量只能被当前线程访问,其他线程无法访问和修改。也就是说:将线程公有化变成线程私有化。

    java面试题

    ThreadLocal(线程变量副本) Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量。 采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的...

    ThreadLocal:如何优雅的解决SimpleDateFormat多线程安全问题

    目录SimpleDateFormat诡异bug复现SimpleDateFormat诡异bug字符串日期转Date日期(parse)Date日期转String类型(format)SimpleDateFormat出现bug的原因如何解决SimpleDateFormat多线程安全问题局部变量使用...

    JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】

    每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程读/写共享变量的副本 本地内存时JMM的一个抽象概念, 并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的 硬件和编译器优化

    java多线程安全性基础介绍.pptx

    各线程之间变量不可见,线程通信通过共享主内存实现。 volatile 仅保证可见性 作用 不会被缓存在寄存器或其他对cpu不可见的地方 强制其他线程读主内存 编译器和运行时不会讲该变量的操作与其他内存操作一起重...

    Java中的线程同步与ThreadLocal无锁化线程封闭实现

    主要介绍了Java中的线程同步与ThreadLocal无锁化线程封闭实现,Synchronized关键字与ThreadLocal变量的使用是Java中线程控制的基础,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics