FastThreadLocal(一)

FastThreadLocal

JDK原生ThreadLocal

在日常并发编程中,锁,CAS和线程局部变量一直是实用的三板斧。Java提供的线程局部不变量就是ThreadLocal。每个线程局部变量都只可以被所属的线程进行读写,优美地规避了线程安全问题。

ThreadLocal的使用也极其简单。(已经会的读者可跳过)

public class ThreadLocalTest {

    //展示的数据类
    public static class Data{
        int data;
        public Data(int data){
            this.data=data;
        }

        @Override
        public String toString() {
            return "Data: "+data;
        }

        public int getData() {
            return data;
        }

        public void setData(int data) {
            this.data = data;
        }
    }

    //线程局部变量
    public static final ThreadLocal<Data> data=new ThreadLocal<Data>(){
        @Override
        protected Data initialValue() {
            return new Data(1);
        }
    };

    public static void main(String[] args) {

        //线程1修改data数值,并模拟调度沉睡一秒
        Runnable runnable1=()->{
            Data data=ThreadLocalTest.data.get();
            data.setData(2);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(ThreadLocalTest.data.get());
        };

        //线程2再次修改data数值,并模拟调度,但只沉睡0.5秒
        Runnable runnable2=()->{
            Data data=ThreadLocalTest.data.get();
            data.setData(7);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(ThreadLocalTest.data.get());
        };

        new Thread(runnable1).start();
        new Thread(runnable2).start();

    }
}

我们可以看到上述线程都只能修改数据在本线程内的数据副本,不会影响到其他线程,避免了脏数据。

JDK ThreadLocal的源码也十分容易理解。已经有许多博客对此进行相关阐述,这里只进行大概讲解。

在每一个Thread对象中对维持着一个ThreadLocalMap,用来存储数据

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap的key是ThreadLocal,value是我们需要存储的值。代码如下:

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {

            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  }

每当我们调用get()方法是,代码首先定位到当前线程的ThreadLocalMap,再把调用get()方法的ThreadLocal对象作为key去ThreadLocalMap中查找值,并返回。代码如下:

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();
}

ThreadLocal不足

第一,内存泄漏

在上一小节的代码中,我们可以看到Entry是一个对于ThreadLocal的弱引用。

static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {

            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
  }

为什么呢?很简单,假设我们现在需要存储一个值线程局部变量到当前线程专属的局部变量容器中ThreadLocalMap。ThreadLocalMap本质上是一个Entry数组。Entry对象持有一个ThreadLocal的弱引用和一个对Object(value)对象的强引用。如果一个对象仅被弱引用持有,那么JVM将回收该对象。同时,Entry对象还持有一个Object的引用。现在使用完一个ThreadLocal之后,我们可以直接设置

threadLocal=null

因为Entry对象仅仅对ThreadLocal进行弱引用持有,因此JVM会回收ThreadLocal对象,相当于清空了key,方便以后存储。

上面,我们注意到Entry对象持有着value对象的强引用。因此,假设现在ThreadLocalMap仍然被线程持有。观察内存,ThreadLocalMap可能持有大量的key为空的Entry对象,在Entry的value较大时,将造成严重的内存泄漏。

ThreadLocal的第二不足是hash未命中时的问题。ThreadLocal是一个Entr数组。当调用get()方法时,代码首先对ThreadLocal进行hash计算,到数组中进行值的获取和返回。当hash未命中时,那么ThreadLocal将进行一次遍历。

FastThreadLcoal设计

Netty重新设计了ThreadLocal,并命名为FastThreadLocal。FastThreadLocal是Netty对ThreadLocal性能的一次优化。

由于篇幅原因,这里我们将对FastThreadLocal的设计进行简单介绍。详细介绍将放在下一篇。

整个FastThreadLocal体系设计FastThreadLocalThread、FastThreadLocal和InternalThreadLocalMap三个类。其中FastThreadLocalThread是对原生态Thread的一次包装。如同Thre对象都有一个唯一的ThreadLocalMap进行局部变量存储的容器,FastThreadLocalThread则是维护了一个InternalThreadLocalMap容器。上一小节提到,ThreadLocal的hash未命中时的性能问题,FastThreadLocal最主要优化就是每一个局部变量的索引都是确定的。

public FastThreadLocal() {
    index = InternalThreadLocalMap.nextVariableIndex();
}

而这个索引则是有一个原生态的AtomicInteger来进行计数的。

未完待续。。。

原文地址:https://www.cnblogs.com/SourMango/p/9098178.html

时间: 2024-08-10 08:15:51

FastThreadLocal(一)的相关文章

jdk自带的ThreadLocal和netty扩展的FastThreadLocal比较总结

最近在分析一潜在内存泄露问题的时候,jmap出来中有很多的FastThreadLocalThread实例,看了下javadoc,如下: A special variant of ThreadLocal that yields higher access performance when accessed from a FastThreadLocalThread. Internally, a FastThreadLocal uses a constant index in an array, in

Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)----&gt;第5节: 同线程回收对象

Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第五节: 同线程回收对象 上一小节剖析了从recycler中获取一个对象, 这一小节分析在创建和回收是同线程的前提下, recycler是如何进行回收的 回顾第三小节的demo中的main方法: public static void main(String[] args){ User user1 = RECYCLER.get(); user1.recycle(); User user2 = RECYCLER

Netty源码分析第8章(高性能工具类FastThreadLocal和Recycler)----&gt;第6节: 异线程回收对象

Netty源码分析第八章: 高性能工具类FastThreadLocal和Recycler 第六节: 异线程回收对象 异线程回收对象, 就是创建对象和回收对象不在同一条线程的情况下, 对象回收的逻辑 我们之前小节简单介绍过, 异线程回收对象, 是不会放在当前线程的stack中的, 而是放在一个WeakOrderQueue的数据结构中, 回顾我们之前的一个图: 8-6-1 相关的逻辑, 我们跟到源码中: 首先从回收对象的入口方法开始, DefualtHandle的recycle方法: public

FastThreadLocal

ThreadLocal 使用场景 使用场景是在于同一个类,但是会开多个线程执行,但是每一个线程可以保持不同的变量状态. 做法如上图,线程类Thread有成员变量ThreadLocal.ThreadLocalMap,用来存储该线程中的所有的ThreadLocal变量,初始化是一个Entry数组. 内存泄漏 static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with t

惊:FastThreadLocal吞吐量居然是ThreadLocal的3倍!!!

说明 接着上次手撕面试题ThreadLocal!!!面试官一听,哎呦不错哦!本文将继续上文的话题,来聊聊FastThreadLocal,目前关于FastThreadLocal的很多文章都有点老有点过时了(本文将澄清几个误区),很多文章关于FastThreadLocal介绍的也不全,希望本篇文章可以带你彻底理解FastThreadLocal!!! FastThreadLocal是Netty提供的,在池化内存分配等都有涉及到!? 关于FastThreadLocal,零度准备从这几个方面进行讲解: F

Netty高性能组件——FastThreadLocal源码解析(细微处见真章)

1. 前言 netty自行封装了FastThreadLocal以替换jdk提供的ThreadLocal,结合封装的FastThreadLocalThread,在多线程环境下的变量提高了ThreadLocal对象的查询以及更新效率. 下文,将通过对比ThreadLocal与FastThreadLocal,通过源码解析,探究FastThreadLocal与FastThreadLocalThread的搭配使用后性能的奥秘. 2. ThreadLocalMap ThreadLocalMap是Thared

Netty 高性能之道 - Recycler 对象池的复用

前言 我们知道,Java 创建一个实例的消耗是不小的,如果没有使用栈上分配和 TLAB,那么就需要使用 CAS 在堆中创建对象.所以现在很多框架都使用对象池.Netty 也不例外,通过重用对象,能够避免频繁创建对象和销毁对象带来的损耗. 来看看具体实现. 1. Recycler 抽象类简介 该类 doc: Light-weight object pool based on a thread-local stack. 基于线程局部堆栈的轻量级对象池. 该类是个容器,内部主要是一个 Stack 结构

Netty 解码器抽象父类 ByteToMessageDecoder 源码解析

前言 Netty 的解码器有很多种,比如基于长度的,基于分割符的,私有协议的.但是,总体的思路都是一致的. 拆包思路:当数据满足了 解码条件时,将其拆开.放到数组.然后发送到业务 handler 处理. 半包思路: 当读取的数据不够时,先存起来,直到满足解码条件后,放进数组.送到业务 handler 处理. 而实现这个逻辑的就是我们今天的主角:ByteToMessageDecoder. 看名字的意思是:将字节转换成消息的解码器.人如其名.而他本身也是一个入站 handler,所以,我们还是从他的

Java开发之深入剖析Netty框架源码实战视频教程

第1章 课程介绍介绍本课程需要的前提知识和内容概要 第2章 Netty基本组件使用一个简单的socket例子概括Netty里面的基本组件,包括NioEventLoop,Channel,ByteBuf,Pipeline,ChannelHandler 第4章 NioEventLoop分析Netty reactor线程处理过程,包括事件监听,事件处理,常规任务处理和定时任务处理 第5章 新连接接入分析新连接接入以及绑定reactor线程,绑定到selector的过程 第6章 pipeline分析pip