zoukankan      html  css  js  c++  java
  • 【Java】ThreadLocal细节分析

          ThreadLocal通过中文解释就是线程本地变量,是线程的一个局部变量。根据哲学家黑格尔“的存在即合理”的说法,ThreadLocal的出现肯定是有它的意义,它的出现也是因为多线程的一个产物。ThreadLocal既然跟线程有关系,那肯定得先对线程了解了解。

          从网上找来了一句话:Java线程的创建,除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)和内部数据结构提供一些本机内存。

          先来学习一下什么是堆栈空间

          

          以上是Java虚拟机运行时数据区的结构图,从图中看出,堆栈(即图中的虚拟机栈)是Java运行时内存分配的一部分,这个堆栈的大小可以用过JVM启动参数Xss设置(JDK5+版本默认好像是1M),那么,这个堆栈是干什么用的呢?大学期间有认真学习过一些技术课程如操作系统、数据结构、C语言等学科的话,不在话下。可惜,我不是这种学生。为了能理解堆栈的一些相关知识,我快速阅览相关知识继续学习(现在才知道大学的知识真的是很用,如果再给我4年时间,算了,现在学习也为时不晚)。一句话,它是线程执行方法(字节码)的地方,每创建一个线程实例,就是分配固定大小(Xss设置参数)的内存空间给线程执行方法用,具体结构图如下:

    上图的每一个栈帧就相当于一个方法的执行,更多可参考《深入理解Java虚拟机》8.2章节。

    上面已经大概的了解了一下堆栈,那就再来看看线程的堆栈与本地变量的一个关系结构图:

          我们知道,线程实例也是一个对象,对象都是存放在堆里面的。从上图可以看出,当创建一个新的线程,那么就会有一个相对应的堆栈空间创建,那个stack部分就可以很好的解析上文提到的一句话“Java线程的创建,除了堆栈空间,每个线程还需要为线程本地存储(thread-local storage)和内部数据结构提供一些本机内存”。这个stack区域就是堆栈空间,而在这个堆栈空间里面有两个直线了堆栈对象的引用,一个是线程实例的应用,另一个就是本地变量的引用。

    总结:

    1、  堆栈的溢出不会直接因为ThreadLocal保存对象的过大的导致堆栈溢出,因为线程堆栈保存的只是引用。真正的对象还是在堆里面,如果对象大到超出堆内存的限制,反而会导致堆溢出。

    2、  从源码可以看出,每个Thread中都存在一个Map,Map的类型是ThreadLocal.ThreadLocalMap。Map中的key为一个Threadlocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向Threadlocal。 当把Threadlocal实例置为null以后,没有任何强引用指向Threadlocal实例,所以Threadlocal将会被gc回收。但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用。只有当前thread结束以后, current thread就不会存在栈中,强引用断开,Current Thread、 Map,、value将全部被GC回收。

    3、  堆栈的生命周期是跟随着线程的生命周期,当线程池中的线程实例还在,堆栈也不会被回收或者清空,所以,原来的ThreadLocalMap还是存在的。所以,当线程池与ThreadLocal同时使用的时候特别需要注意。

  • 相关阅读:
    C语言笔记
    js学习笔记
    Javascript学习笔记
    Java基础知识
    使用 StackExchange.Redis 封装属于自己的 RedisHelper
    StackExchange.Redis 使用资料
    .NET平台下Redis使用(三)【ServiceStack.Redis学习】
    .NET平台下Redis使用(二)【StackExchange.Redis学习】
    Redis 详解 (一) StackExchange.Redis Client
    .NET中使用Redis
  • 原文地址:https://www.cnblogs.com/zhangyingai/p/7087397.html
Copyright © 2011-2022 走看看