JUC P8 ThreadLocal 基础+代码

news2025/1/21 15:24:31

JUC P8 ThreadLocal 基础+代码

教程:https://www.bilibili.com/video/BV1ar4y1x727?p=100

引出问题

ThreadLocal 和 TreadLocalMap 数据结构关系?
ThreadLocal 中的 key 是弱引用,为什么?
ThreadLocal 内存泄漏问题是什么?
ThreadLocal 中最后为什么要加 remove 方法?

1. ThreadLocal 描述

ThreadLocal 提供线程局部变量。这些变量与正常的变量不同,因为每个线程在访问 ThreadLocal 实例的时候(通过 get 或 set 方法)都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,使用它的目的是希望将状态(例如,用户 ID 或事务 ID)与线程关联起来。

2. 应用

2.1 五个销售卖房子,集团高层只关心销售总量的准确统计数

实例方法上锁

@Slf4j(topic = "c.Test")
public class Test {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(10) + 1;
                log.info("{} 卖出了 {} 套房子", Thread.currentThread().getName(), size);
                for (int j = 0; j < size; j++) {
                    house.saleHouse();
                }
            }, "t" + i).start();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("{} 共卖出了 {} 套房子", Thread.currentThread().getName(), house.saleCount);
    }
}

class House {
    public int saleCount;
    public synchronized void saleHouse() {
        saleCount++;
    }
}

在这里插入图片描述

2.2 五个销售都有自己的销售额指标,自己专属自己的,不和别人掺和

@Slf4j(topic = "c.Test")
public class Test {
    public static void main(String[] args) {
        House house = new House();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                int size = new Random().nextInt(10) + 1;
                for (int j = 0; j < size; j++) {
                    house.saleVolumeByThreadLocal();
                }
                log.info("{} 卖出了 {} 套房子", Thread.currentThread().getName(), house.saleVolume.get());
            }, "t" + i).start();
        }
    }
}

class House {
    ThreadLocal<Integer> saleVolume = ThreadLocal.withInitial(() -> 0);
    public void saleVolumeByThreadLocal() {
        saleVolume.set(saleVolume.get() + 1);
    }
}

在这里插入图片描述

这样写的隐患:
线程池的场景下因为线程会复用,如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄漏等问题。尽量在代理中使用 try-finally 块进行回收。

try {
    int size = new Random().nextInt(10) + 1;
    for (int j = 0; j < size; j++) {
        house.saleVolumeByThreadLocal();
    }
    log.info("{} 卖出了 {} 套房子", Thread.currentThread().getName(), house.saleVolume.get());
} finally {
    house.saleVolume.remove();
}

2.3 线程池场景

不清理 ThreadLocal 变量

@Slf4j(topic = "c.Test")
public class Test {
    public static void main(String[] args) {
        MyData myData = new MyData();
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.submit(() -> {
                    Integer before = myData.threadLocalField.get();
                    myData.add();
                    Integer after = myData.threadLocalField.get();
                    log.info("before: {} -> after: {}", before, after);
                });
            }
        } finally {
            threadPool.shutdown();
        }
    }
}

class MyData {
    public ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);

    public void add() {
        threadLocalField.set(threadLocalField.get() + 1);
    }
}

在这里插入图片描述
每个线程每执行完自己的任务后就应该恢复到原始状态,否则会影响到后续的线程。

清理 ThreadLocal 变量

@Slf4j(topic = "c.Test")
public class Test {
    public static void main(String[] args) {
        MyData myData = new MyData();
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.submit(() -> {
                    try {
                        Integer before = myData.threadLocalField.get();
                        myData.add();
                        Integer after = myData.threadLocalField.get();
                        log.info("before: {} -> after: {}", before, after);
                    } finally {
                        // 清除线程变量
                        myData.threadLocalField.remove();
                    }
                });
            }
        } finally {
            threadPool.shutdown();
        }
    }
}

class MyData {
    public ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);

    public void add() {
        threadLocalField.set(threadLocalField.get() + 1);
    }
}

在这里插入图片描述

3. 面试题

3.1 Thread,ThreadLocal,ThreadLocalMap 关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ThreadLocalMap 从字面上可以看出这是一个保存 ThreadLocal 对象的 map,以 ThreadLocal 为 key。

JVM 内部维护了一个线程版的 Map<ThreadLocal, Value>,(通过 ThreadLocal 对象的 set 方法,结果把 ThreadLocal 对象自己当作 key,放进了 ThreadLocalMap 中),每个线程要用到这个线程的时候,用当前的线程去 Map 中获取,通过这样让每个线程都有了自己独立的变量,人手一份,竞争条件被彻底消除,在并发模式下是绝对安全的变量。

3.2 ThreadLocal 内存泄漏问题

内存泄漏:占着茅坑不拉屎,不再使用的对象占用的内存不能被回收。

为什么要用弱引用?

To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys. However, since reference queues are not used, stale entries are guaranteed to be removed only when the table starts running out of space.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 最佳实践

  1. 初始化方式:
ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() -> 0);
  1. 建议把 ThreadLocal 修饰为 static
    在这里插入图片描述
    在这里插入图片描述

  2. 线程使用 ThreadLocal 完之后必须进行 remove 回收

  3. 使用条件
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/991143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git标签基础

打标签:git可以给仓库历史中某个提交打上标签,以示重要,比较有代表人们会使用这个功能来标记发布结点(V1.0,V2.0) 列出本地标签: git tag --list git tag -l "V1.85*" 列出远端仓库的中所有标签 git ls-remote --tags给标签添加一些描述信息 git tag -a v1.3 -m …

Amazon Fargate 使用 Seekable OCI 实现更快的容器启动速度

虽然在部署和扩展应用程序时&#xff0c;使用容器进行开发的方式已日趋流行&#xff0c;但仍有一些领域可以改进。扩展容器化应用程序的主要问题之一是启动时间长&#xff0c;尤其是在纵向扩展期间&#xff0c;需要添加较新的实例。此问题可能会对客户体验&#xff08;例如&…

AST还原实战|第二届猿人学js逆向比赛第三题混淆还原详解

关注它&#xff0c;不迷路。 本文章中所有内容仅供学习交流&#xff0c;不可用于任何商业用途和非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请联系作者立即删除&#xff01; 1. 目标地址 https://match2023.yuanrenxue.cn/topic/3 其加密参数tok…

精益创业的三个测量方法

精益创业三个测量工具【安志强趣讲282期】 趣讲大白话&#xff1a;没法度量就没法改进 **************************** 工具1&#xff1a;AB对比测试 就是产品有两个或多个版本 然后通过外部客户或内部人员评测 可以组织天使用户群&#xff1a;愿意参与的专业人士 工具2&#x…

【Linux】工具GCC G++编译器轻度使用(C++)

目录 一、关联知识背景 二、GCC如何的编译过程 【2.1】预处理(进行宏替换) 【2.2】编译(生成汇编) 【2.3】连接(生成可执行文件或库文件) 三、GCC命令的常用选项 四、动静态链接 一、关联知识背景 gcc 与 g 分别是 gnu 的 c & c 编译器 gcc/g 在执行编译工作的时候…

Leetcode 剑指 Offer II 043. 完全二叉树插入器

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 完全二叉树是每一层&#xff08;除最后一层外&#xff09;都是完…

并发聊天服务器编写

并发聊天服务器 package mainimport ("fmt""net""strings""time" )// 结构体 type Client struct {C chan string //用户发送数据的管道Name string //用户名Addr string //网络地址 }// 保存在线用户 cliAddr -->cli…

Wireshark技巧[监听串口包]

监听串口包 本文摘录于&#xff1a;https://blog.csdn.net/qq_20405005/article/details/79652927只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 这里要保证安装了USBpcap: 打开USBpcap后一半都要输入过滤条件,否则USB太多数据了,比如…

输入验证在防止安全漏洞方面的重要性

在当今快速发展的数字环境中&#xff0c;技术推动创新和便利&#xff0c;确保我们数字资产的安全仍然是一个关键问题。创建强大的应用程序安全性的核心在于输入验证的基本且最重要的概念。 在这篇博文中&#xff0c;我们将介绍输​​入验证的重要性及其对加强我们的数字防御以…

python可视化模块—快速利用matplot绘制图表

文章目录 一、Matplotlib基本介绍二、两种绘图方式区别&#xff08;plt.*** 和ax.***&#xff09;三、如何使用Matplotlib绘图1、画布—绘画的画板2、配置—更个性化的绘图全局配置局部配置面向对象绘图过程&#xff1a;ax代表子图变量过程式绘图过程 四、常用绘图图形如何选择…

Java集合学习(2023年超详细)

java集合学习目录 一、基本概要0. 辅助工具类0.1 Collection 和 Collections 有什么区别&#xff1f;0.2 comparable 和 comparator的区别&#xff1f; 1.什么是集合2.集合的分类2.1 Collection接口2.2 Map接口 二、集合框架底层数据结构1. &#x1f60a;Collection1.1 ❤List1…

【数据结构】3000字剖析链表及双向链表

文章目录 &#x1f490; 链表的概念与结构&#x1f490;链表的介绍&#x1f490;链表的模拟实现 &#x1f490;双向链表&#x1f490;双向链表的模拟实现 &#x1f490;链表常用的方法&#x1f490;链表及顺序表的遍历&#x1f490;ArrayList和LinkedList的差异 &#x1f490; …

Python常用库(五):图像处理【Pillow】

1. Pillow 1.1 介绍 Pillow 是第三方开源的 Python 图像处理库&#xff0c;它支持多种图片格式&#xff0c;包括 BMP、GIF、JPEG、PNG、TIFF 等。Pillow 库包含了大量的图片处理函数和方法&#xff0c;可以进行图片的读取、显示、旋转、缩放、裁剪、转换等操作。在后续的深度学…

python开发基础篇2——登陆机制

文章目录 一、管理平台页面布局二、登录页面2.1 token登录2.2. kubeconfig登录2.3 添加装饰器 一、管理平台页面布局 应用名称&#xff1a; dashboard&#xff1a;存放公共 k8s&#xff1a; Node&#xff1a;K8s集群计算节点。Namespaces&#xff1a;命名空间&#xff0c;用于…

ABAP WS_DELIVERY_UPDATE 报错 BS013

在使用 WS_DELIVERY_UPDATE 创建内向交货单时 报错&#xff1a;System status ESTO is active (EQU ***) 可以用事务代码IE03--->历史去看下 你可以手动将它的状态去改回EDEL 或者 SLOR IE02--> Special serial no. functions --> Manual transaction.

C#,《小白学程序》第十七课:随机数(Random)第四,移动平均值(Moving Average)的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十七课&#xff1a;随机数&#xff08;Random&#xff09;第四&#xff0c;移动平均值的计算方法与代码 /// 继续学习数据统计&#xff0c;移动平均值的计算方法 /// 移动平均值就是一定步长内数值的平均值&#xff0c;用…

Llama 2 论文《Llama 2: Open Foundation and Fine-Tuned Chat Models》阅读笔记

文章目录 Llama 2: Open Foundation and Fine-Tuned Chat Models1.简介2.预训练2.1 预训练数据2.2 训练详情2.3 LLAMA 2 预训练模型评估 3. 微调3.1 supervised Fine-Tuning(SFT)3.2 Reinforcement Learning with Human Feedback (RLHF)3.2.1 人类偏好数据收集3.2.2 奖励模型训…

Matlab 如何选择窗函数和 FFT 的长度

Matlab 如何选择窗函数和 FFT 的长度 1、常用的四种窗函数 对于实际信号序列&#xff0c;如何选取窗函数呢&#xff1f;一般来说&#xff0c;选择第一旁瓣衰减大&#xff0c;旁瓣峰值衰减快的窗函数有利于緩解截断过程中产生的頻泄漏问题。但具有这两个特性的窗函数&#xff0…

[BFS] 广度优先搜索

1. 数字操作 常见的模板 // 使用一个数组判断元素是否入过队 int inqueue[N] {0}; // 层数或者可以称为深度 int step 0; // 判断是否可以入队的条件 int isvalid(){ } BFS(int x){ // 将初始的元素压入队列 // 注意每次压队的时候都要将inque[x] 1,表明入队过…

python实现adb辅助点击屏幕工具

#!/usr/bin/env python # -*- coding: utf-8 -*-import re import os import time import subprocess import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk# 设置ADB路径&#xff08;根据你的系统和安装路径进行调整&#xff09; ADB_PATH C…