JUC补充(ThreadLocal、completableFuture)

提供线程内局部变量,不同的线程之间不会互相干扰,这种变量在线程的声明周期内起作用,减少同一线程内多个函数或组件之间一些公共变量传递的复杂度

  • 线程并发:多线程并发场景下
  • 传递数据:我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量(降低耦合度)
  • 线程隔离:每个线程的变量都是独立的,互不影响

常用方法

ThreadLocal():构造方法

get():获取变量

set():设置变量

remove():移除变量

内部结构

  • 每个thread线程内部都有一个Map(ThreadLocalMap)
  • map里面存储ThreadLocal对象(key)和线程的变量副本(value)
  • Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值
  • 对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰

每个线程内部都有一个 ThreadLocalMap,map 中以 ThreadLocal 对象作为 key,因此同一个线程可以通过多个 ThreadLocal 存储多个线程隔离变量。

弱引用和内存泄露

内存溢出:内存不够

内存泄漏:不用的对象没有被释放

引用链:CurrentThreadRef->CurrentThread->Map->Entry(key-->ThreadLocal,value->data)

key 使用弱引用,是为了让 ThreadLocal 对象能够被回收,避免因为 Entry 持有强引用导致 ThreadLocal 本身无法释放。

真正内存泄露的是value,当使用线程池,线程不会删除,ThreadLocalMap复用,我们上次执行业务逻辑的Entry已经没有用了,key能够通过弱引用回收,但value是强引用不能被回收

解决:手动remove()方法,没有运用线程池

问题:为什么不设置value为弱引用

答:value是业务数据,我们如果设置为弱引用,当我们下次拿数据时,这个数据可能已经被垃圾回收了(没有强引用),而ThreadLocal对象创建时就会用强引用

ThreadLocalMap 不会在每次 get/set 时遍历整个 map 清理脏 Entry,而是采用惰性清理策略,仅在访问冲突位置或扩容 rehash 时局部清理 key 为 null 的 Entry,以降低性能开销。

Hash冲突解决

获取当前ThreadLocal哈希值的代码

java 复制代码
 private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

static修饰,说明全局ThreadLocal都是通过这个方法获取哈希码,因此用原子累加器

AtomicInteger 确保了全局多个线程并发创建 ThreadLocal 时,生成的哈希码绝对唯一、不冲突。

0x61c88647(黄金分割数) 确保了这些连续生成的哈希码,在面对 2n2^n2n 大小的开放寻址 Map 时,能够具有近乎完美的离散度,从而把增删改查的效率死死锁在 O(1)O(1)O(1)。

set流程:

  • 根据key计算索引i,找到对应entry
  • 如果存在且key为当前ThreadLocal对象,则覆盖
  • 若存在且key为null,调用replaceStaleEntry来更换这个key为空的Entry
  • 不断循环检测,直到遇到为null的地方,在这个为null的位置新建一个entry,并且插入,同时size++
  • 最后调用cleansomeSlots,清理key为null的Entry,最后返回是够清理了Entry,接下来再判断size是否>=thresgold达到了rehash的条件,达到的话就调用rehash函数执行一次全表扫描清理

volatile内存屏障

可见性:

写完后立即刷新回主内存并及时发出通知,其他线程去主内存拿最新数据

有序性:

禁止指令重排

重排序:编译器和处理器为了优化程序性能而对指令序列进行重排序的一种手段,有时候会改变程序语句的先后顺序

cpu或编译器在对内存随机访问的操作中的一个同步点

内存屏障之前的所有写操作都要会写到主内存

内存屏障之后的所有读操作都能获得内存屏障之前的所有写曹组的最新结果(可见性)

Future

异步任务接口(Future)

FutureTask

Future实现类

特点:多线程、有返回、异步执行、可以抛异常

优点:多线程、有返回,可以抛异常

缺点:

  • get()方法是阻塞的
  • while(true)+isDone():轮询判断,还是有cpu空转消耗

completableFuture

新功能:回到通知、创建异步任务、多个任务前后依赖可以组合处理、对计算速度选最快

同时实现Future、CompletionStage接口

CompletionStage代表异步计算过程中的某一阶段,一个阶段完成以后会触发另外一个阶段

核心静态方法

  • runAsync无返回值 参数:Runnable,Executor
  • supplyAsync有返回值 参数:Supplier,Executor

没有指定线程池,用默认线程池ForkJoinPool.commonPool()

默认线程池ForkJoinPool.commonPool()是守护线程,当主线程结束,异步任务也会随之结束,所以要使用自定义线程池

常用方法

  • 获取结果:

    get():抛异常(受检异常)(必须try-catch)

    get(long,Unit)

    join():不抛异常(推荐)

    getNow(T valueIfAbsent):如果没有计算完,返回指定值

    complete(T value):打断线程,get返回值设置为value,方法返回值为true(被打断),false

  • 对计算结果进行处理

    thenApply:出现异常,之后步骤结束

    handle:有异常也可以继续执行

    都是用于计算结果存在依赖关系,使线程串行化

  • 对计算结果进行消费

    thenAccept

    和之前一样,只是没有返回值

线程池说明

对计算速度选用

playA.applyToEither(playB,回调函数)

计算结果合并

completableFuture1.thenCombine(conpletableFuture2,回调函数)

相关推荐
dyxal4 小时前
Louvain 算法:让网络自己“报团取暖”的发现者
开发语言·算法
计算机安禾4 小时前
【c++面向对象编程】第41篇:函数模板与类模板:泛型编程的基石
开发语言·c++·算法
熊猫_豆豆4 小时前
麦克斯韦方程组(电磁效应Python展示)
开发语言·python·电磁感应·麦克斯韦方程组
Seven974 小时前
两小时入门Sentinel
java
SilentSamsara5 小时前
属性查找顺序:实例 → 类 → 父类的完整 MRO
开发语言·python·算法·青少年编程
tongluowan0075 小时前
Java中atomic底层原理 - ABA 问题与解决方案
java·juc·atomic
运维行者_5 小时前
云计算连接性与互操作性
服务器·开发语言·网络·web安全·网络基础设施
郝学胜-神的一滴5 小时前
Qt 高级开发 010: 从跨界面传值到自定义信号
开发语言·c++·qt·程序人生·用户界面
社交怪人5 小时前
【浮点数相除的余】信息学奥赛一本通C语言解法(题号1029)
c语言·开发语言