ThreadLocal原理、结构、源码解析
文章目录
- 一、Thread简介
- 1.什么是ThreadLocal
- 2.为什么要是用ThreadLocal
- 2.1Synchronized、Lock保证线程安全
- 2.2ThreadLocal保证线程安全
- 3.ThreadLocal和Synchronized的区别
- 二、ThreadLocal原理
- 1.Thread抽象内部结构
- 2.ThreadLocal源码
- 2.1Thread、ThreadLocal、ThreadLocalMap、Entry之间关系
- 2.2ThreadLocal类的set()方法
- 2.3ThreadLocal类的get()方法
- 2.4ThreadLocal类的remove()方法
- 2.5面试:说一说ThreadLocal原理、Thread如何实现线程隔离的
- 三、深究ThreadLocal
- 1.为什么不直接用线程id作为ThreadLocalMap的key呢?
- 2.ThreadLocal导致内存泄漏的原因
- 3.ThreadLocalMap在设计中有没有考虑到内存泄漏这点呢?
- 4.在使用ThreadLocal中,我们应该注意什么
- 5.key是弱引用,GC回收会影响ThreadLocal的正常工作嘛
- 6.Entry的key为什么要设计成弱引用呢,为什么不使用强引用
- 6.1Entry的key如果使用强引用
- 6.2为什么要设计成弱引用
- 6.3四种引用类型
- 7.我们希望父子线程之间共享数据,应该怎么做呢
- 7.1InheritableThreadLocal
- 7.2InheritableThreadLocal是如何实现父子线程之间共享的
一、Thread简介
1.什么是ThreadLocal
ThreadLocal,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量,从而起到线程隔离的作用,保证了线程安全。
- 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
- 每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
2.为什么要是用ThreadLocal
保证线程安全,并发场景下,会出现多个线程共享一个变量的场景,这种场景可能会出现线程安全性问题,我们可以采取加锁的方式(Synchronized、Lock)方式,也可以使用使用ThreadLocal方式来避免线程安全问题。
2.1Synchronized、Lock保证线程安全
采用加锁方式保证线程安全到导致系统变慢。共享变量某个时刻只能由一个线程访问,其他线程需要等到该线程释放锁才能访问,影响系统性能。

2.2ThreadLocal保证线程安全
使用ThreadLocal。使用ThreadLocal类访问共享变量时,会在每个线程的本地,都保存一份共享变量的拷贝副本。多线程对共享变量修改时,实际上操作的是这个变量副本,从而保证线性安全。

3.ThreadLocal和Synchronized的区别
- ThreadLocal和Synchronized都是为了解决并发问题。
- Synchronized用于线程共享, 而ThreadLocal用于线程隔离。
- Synchronized是时间换空间,ThreadLocal是空间换时间。
- Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。
二、ThreadLocal原理
1.Thread抽象内部结构
简单理解(看不懂先往下看再回顾):
- Thread类中有ThreadLocal.ThreadLocalMap threadLocals属性
- ThreadLocalMap 是Thread的静态内部类,它维护着Entry对象数组,每个Entry代表一个ThreadLocalMap对象
- Entry是ThreadLocalMap的静态内部类,存储采用的是k-v形式,key为ThreadLocal,value为我

2.ThreadLocal源码
2.1Thread、ThreadLocal、ThreadLocalMap、Entry之间关系
先根据源码砍砍他们之间的关系
- Thread的成员变量ThreadLocal.ThreadLocalMap
- ThreadLocal.ThreadLocalMap是ThreadLocal的静态内部类
- Entry是ThreadLocalMap的静态内部类,Entry继承了弱引用,也就是说,如果外部没有强引用关联的话,下一次GC时会被回收。
- 在Entry是一个k-v形式,内部使用ThreadLocal作为key,使用我们设置的value作为value。
- Entry[] table为ThreadLocal的属性,由ThreadLocalMap维护
//Thread类
class Thread implements Runnable {//ThreadLocalMap是Thread的成员变量ThreadLocal.ThreadLocalMap threadLocals = null;}//ThreadLocal类
public class ThreadLocal<T> {//Entry数组是ThreadLocal的成员变量,该数组由ThreadLocalMap维护private Entry[] table;//静态内部类ThreadLocalMap
static class ThreadLocalMap {//Entry是ThreadLocalMap的静态内部类,注意,Entry继承了弱引用(如果没有被强引用关联,下一次GC会被回收)static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
}
}
2.2ThreadLocal类的set()方法
通过源码可以看到,ThreadLocalMap维护了Entry数组,Entry是k-v形式,key为ThreadLocal,value为我们传入的值,当同一个线程对同一个ThreadLocal进行两次set时,value会被覆盖。
- 获取当前线程,根据当前线程获取ThreadLocalMap
- 判断ThreadLocalMap是否存在
- 存在则将ThreadLocal作为key,传入的值为value,存入ThreadLocalMap的Entry中
- 不存在则根据ThreadLocalMap构造函数创建ThreadLocalMap并将ThreadLocal作为key,传入的值为value,存入ThreadLocalMap的Entry中
public void set(T value) {Thread t = Thread.currentThread();//获取当前线程tThreadLocalMap map = getMap(t);//根据当前线程获取到ThreadLocalMapif (map != null)map.set(this, value);//如果map不为空,则将当前对象ThreadLocal作为key,传入的值为value存入ThreadLocalMap中elsecreateMap(t, value);//如果map为空,则创建ThreadLocalMap对象后,再将k-v存入ThreadLocalMap中}//该方法位于ThreadLocal中ThreadLocalMap getMap(Thread t) {return t.threadLocals;//根据线程t获取Thrad的ThreadLocalMap}//该方法位于ThreadLocal中void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);//调用ThreadLocalMap构造函数,this表示当前类ThreadLocal}//该方法位于ThreadLocal中,该方法位于ThreadLocal中构造函数维护这Entry数组tableThreadLocalMap(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);}
2.3ThreadLocal类的get()方法
- 获取当前线程t
- 根据线程t获取ThradLocalMap map
- map存在则获取Entry,Entry存在获取value
- map不存在,初始化ThradLocalMap并将ThreadLocal作为kay,valeu为null存进ThreadLocalMap中,返回value也就是null。
- (调用get()方法时,ThreadLocalMap没有初始化则会初始化并ThreadLocal作为kay,valeu为null存进ThreadLocalMap中)
public T get() {Thread t = Thread.currentThread();//获取当前线程ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);//map不为空,获取Entryif (e != null) {//entry不为空。返回value@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();//map为空,初始化threadLocals成员变量的值,也就是初始化把thread Local为key,value=null塞进entry}private T setInitialValue() {T value = initialValue(); //初始化value的值Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //以当前线程为key,获取threadLocals成员变量,它是一个ThreadLocalMapif (map != null)map.set(this, value); //K,V设置到ThreadLocalMap中elsecreateMap(t, value); //实例化threadLocals成员变量return value;//返回null}protected T initialValue() {return null;}
2.4ThreadLocal类的remove()方法
- 获取当前线程,根据当前线程获取ThreadLocalMap
- 如果map部位空,则删除map中指定的的Entry
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());//获取当前线程的ThreadLocalMap变量if (m != null)m.remove(this);//对象不为空,则删除
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
private void remove(ThreadLocal<?> key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {e.clear();expungeStaleEntry(i);return;}}
}
2.5面试:说一说ThreadLocal原理、Thread如何实现线程隔离的
ThreadLocal叫本地线程变量,作用就是当多线程访问共享变量时,起到线程隔离的作用,每个线程都有自己的一个副本,且之间不被共享,这种方式采用的是空间换时间的方式。目的是保证线程安全,采用空间换时间的方式保证线程安全。
每个线程Thread都有一个成员变量ThreadLocal.ThreadLocalMap thradLocals。也就是说每个线程都有一个ThreadLocalMap,这个ThreadLocalMap是ThreadLocal的静态内部类,他维护者Entry对象数组,Entry对象存储方式是k-v的形式。k为ThreadLocal,V为我我们set进去的值。
在并发场景下,每个线程在往ThreadLocal里面设置值得时候,实际就是存进自己的thradLocals属性中,以ThreadLocal为key,set进去的值为value。实现线程隔离。
三、深究ThreadLocal
1.为什么不直接用线程id作为ThreadLocalMap的key呢?
ThreadLocalMap是Thread的属性,维护着Entry数组,Entry的key是ThradLocal,value为我们set入的值。
如果将线程id作为key,那么当一个Thread有多个ThreadLocal进行set()的时候,无法区分value是哪个ThreadLocal的,或者说无论多少个ThreadLocal,每次set进去,由于key都是ThreadId,会导致每次set都会被覆盖。
如图所示,一个key对应多个ThreadLocal。ThreadLocal-1 set()的时候key为Thread,ThreadLocal-1 set()的时候key依然为Thread
2.ThreadLocal导致内存泄漏的原因
ThreadLocal导致内存泄漏愿意你有两个
- 使用完后没有remove(), 由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说(例如核心线程池中的线程),如果没有手动删除(调用remove()方法),会导致Entry对象越来越多,从而导致内存泄漏.
- 第二种原因下面讲解
Entry类继承了弱引用,super(k);使ThreadLocal也是一个弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}
ThreadLocal引用示意图

- ThreadLocalMap使用ThreadLocal的弱引用作为key,当ThreadLocal变量被手动设置为null,即一个ThreadLocal没有外部强引用来引用它,当系统GC时,ThreadLocal一定会被回收。
- 这样的话,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value
- 如果当前线程再迟迟不结束的话(比如线程池的核心线程),这些key为null的Entry的value就会一直存在一条强引用链:Thread变量 -> Thread对象 -> ThreaLocalMap -> Entry -> value -> Object 永远无法回收,造成内存泄漏。
当Thread手动设置为null后的因用链

此时,堆中的ThreadLocal只存在弱引用,再下一次GC时会被回收。回收后,Entry中的key=null,这个Entry就没有办法被访问。导致内存泄漏。
3.ThreadLocalMap在设计中有没有考虑到内存泄漏这点呢?
实际上ThreadLocalMap在设计的时候就考虑到这个情况了,所以ThreadLocal的get、set方法中加了一些防护措施。在执行set、get方法的时候,会清楚线程中ThreadLocalMap中key为null的Entry。
ThreadLocal的set()方法防止内存泄漏措施
private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;//触发一次Log2(N)复杂度的扫描,目的是清除过期Entry if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
ThreadLocal的get()方法防止内存泄露措施
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {//去ThreadLocalMap获取Entry,方法里面有key==null的清除逻辑ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;else//其中有key为null的清楚逻辑return getEntryAfterMiss(key, i, e);}private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {Entry[] tab = table;int len = tab.length;while (e != null) {ThreadLocal<?> k = e.get();if (k == key)return e;if (k == null)// Entry的key为null,则表明没有外部引用,且被GC回收,是一个过期EntryexpungeStaleEntry(i);elsei = nextIndex(i, len);e = tab[i];}return null;}
4.在使用ThreadLocal中,我们应该注意什么
- 将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露
- 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
5.key是弱引用,GC回收会影响ThreadLocal的正常工作嘛
- 弱引用:具有弱引用的对象拥有更短暂的生命周期。如果一个对象只有弱引用存在了,则下次GC将会回收掉该对象(不管当前内存空间足够与否)
当然不会,因为还有ThreadLocal强引用着它,是不会被GC回收的,除非手动将ThradLocal置为null

验证
package com.jhq.threadLocal;import java.lang.ref.WeakReference;
/*** @BelongsProject: study* @BelongsPackage: com.jhq.threadLocal* @Author: jianghq* @CreateTime: 2023-02-24 17:02* @Description: ThreadLocal的key既然是弱引用.会不会GC贸然把key回收掉,进而影响ThreadLocal的正常使用? 不会* @Version: 1.0*/
public class WeakReferenceTest {public static void main(String[] args) {Object object = new Object();WeakReference<Object> testWeakReference = new WeakReference<>(object);System.out.println("GC回收之前,弱引用:"+testWeakReference.get());//触发系统垃圾回收for(int i=0;i<100;i++){//我们只能建议GC回收,并不能百分之百保证真的回收System.gc();}System.out.println("GC回收之后,弱引用:"+testWeakReference.get());//手动设置为object对象为nullobject=null;System.gc();System.out.println("对象object设置为null,GC回收之后,弱引用:"+testWeakReference.get());}
}
输出:GC回收之前,弱引用:java.lang.Object@cc34f4dGC回收之后,弱引用:java.lang.Object@cc34f4d对象object设置为null,GC回收之后,弱引用:null

6.Entry的key为什么要设计成弱引用呢,为什么不使用强引用
官方回答
o help deal with very large andlong-lived usages, the hash table entries use WeakReferences for keys.
为了应对非常大和长时间的用途,哈希表使用弱引用的 key。
6.1Entry的key如果使用强引用
先看看引用图

正这种情况,由于ThreadLocalMap生命周期和Thread一样长,使用强引用之后,只要Thrad存在,那么Entry就会一直存在内存中,如果线程为核心线程池中的线程,Entry就会一直存在,导致内存泄漏。
6.2为什么要设计成弱引用
如果Key使用弱引用:ThreadLocal置为null,因为ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value则在下一次ThreadLocalMap调用set,get,remove的时候会被清除。
6.3四种引用类型
- 强引用:我们new出来的对象就是强引用,例如Object o=new Onject();强引用不会被GC回收。
- 软引用:一个对象只有软引用时,内存空间足够的情况下不会被回收,当内存不足时会被回收。
- 弱引用:当对象只有弱引用时,下一次GC时会被回收。
- 虚引用:如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。
7.我们希望父子线程之间共享数据,应该怎么做呢
7.1InheritableThreadLocal
使用InheritableThreadLocal
public class InheritableThreadLocalTest {public static void main(String[] args) {ThreadLocal threadLocal=new ThreadLocal();InheritableThreadLocal inheritableThreadLocal=new InheritableThreadLocal();threadLocal.set("main线程.ThreadLocal");inheritableThreadLocal.set("main线程.InheritableThreadLocal");new Thread(()->{threadLocal.set(Thread.currentThread().getName()+"ThreadLocal");inheritableThreadLocal.set(Thread.currentThread().getName()+"InheritableThreadLocal");}).start();new Thread(()->{System.out.println("当前线程"+Thread.currentThread().getName()+"===="+threadLocal.get());System.out.println("当前线程"+Thread.currentThread().getName()+"===="+inheritableThreadLocal.get());}).start();}
}//输出
当前线程Thread-0====null
当前线程Thread-0====main线程.InheritableThreadLocal
可以看出,ThreadLocal在父子线程之间是不共享的。InheritableThreadLocal可以在父子线程之间共享。但仅限于父子线程之间。
7.2InheritableThreadLocal是如何实现父子线程之间共享的
InheritableThreadLocal是Thread的成员变量,返回类型也是ThreadLocal.ThreadLocalMap
/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
Thread的init方法
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {//>.....衡略//如果父线程的inheritableThreadLocals不为空,则将inheritableThreadLocals赋值给子类if (parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);/* Stash the specified stack size in case the VM cares */this.stackSize = stackSize;/* Set thread ID */tid = nextThreadID();}static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);}相关文章:
ThreadLocal原理、结构、源码解析
文章目录一、Thread简介1.什么是ThreadLocal2.为什么要是用ThreadLocal2.1Synchronized、Lock保证线程安全2.2ThreadLocal保证线程安全3.ThreadLocal和Synchronized的区别二、ThreadLocal原理1.Thread抽象内部结构2.ThreadLocal源码2.1Thread、ThreadLocal、ThreadLocalMap、En…...
分布式之PBFT算法
写在前面 在分布式之拜占庭问题 一文中我们分析了拜占庭问题,并一起看了支持拜占庭容错的口信消息性和签名消息性算法,但是这两种算法都有一个非常严重的问题,就是消息数量太多,通信的成本太大,消息数量复杂度为O(n ^…...
Linux 操作系统——查看/修改系统时区、时间、本地时间修改为UTC
文章目录1.背景描述2.知识储备3.解决步骤1. 查看当前时区2.修改设置Linux服务器时区3.复制相应的时区文件,替换系统时区文件;或者创建链接文件4. 查看和修改Linux的时间5. 硬件时间和系统时间的 相互同步1.背景描述 最近一个项目日期采用java8的LocalDa…...
CSS数据类型以及符号
css数据类型定义的是css属性中具有代表性的值,在规范的语法格式中,使用关键字外加一对 <和>表示,例如数值类型<number>、色值类型<color>等。 举个例子:background-image这个css属性语法结构如下: …...
LeetCode-54. 螺旋矩阵
题目来源 54. 螺旋矩阵 题目思路 while循环只遍历"环",不成环就不遍历了 四个边界 上边界 top : 0下边界 bottom : matrix.length - 1左边界 left : 0右边界 right : matrix[0].length - 1 矩阵不一定是方阵 top < bottom && left < r…...
【Python入门第十八天】Python For 循环
Python For 循环 for 循环用于迭代序列(即列表,元组,字典,集合或字符串)。 这与其他编程语言中的 for 关键字不太相似,而是更像其他面向对象编程语言中的迭代器方法。 通过使用 for 循环,我们…...
Qt图片定时滚动播放器
目录参考结构PicturePlay.promain.cpppictureplay.hpictureplay.cpppictureplay.ui效果源码参考 Qt图片浏览器 QT制作一个图片播放器 Qt中自适应的labelpixmap充满窗口后,无法缩小只能放大 可以显示jpg、jpeg、png、bmp。可以从电脑上拖动图到窗口并显示出来或者打开…...
李宏毅2023春季机器学习课程
目录2021&2022课程重磅须知我维护的其他项目更新日志课程地址课程资料直链课程作业直链其他优质课程2021&2022课程 CSDN Github 重磅须知 为方便所有网课资料与优质电子书籍的实时更新维护,创建一个在线实时网盘文件夹; 网盘获取方式&#…...
计算机操作系统知识点汇总
计算机操作系统选择填空题,300知识点,包含操作系统概论、处理机管理、内存管理、设备管理、文件管理等,为大学生期末创造奇迹提供无限可能 1、填空题 1、操作系统是对计算机资源进行管理的软件 2、操作系统是提供了处理机管理、 存储器管理…...
【离线数仓-8-数据仓库开发DWD层设计要点-交易域相关事实表】
离线数仓-8-数据仓库开发DWD层设计要点-交易域相关事实表离线数仓-8-数据仓库开发DWD层设计要点-交易域相关事实表一、DWD层设计要点二、交易域相关事实表1.交易域加购事务事实表1.加购事务事实表 前期梳理2.加购事务事实表 DDL表设计分析3.加购事务事实表 加载数据分析1.首日全…...
计算机网络(七):DNS协议和原理,DNS为什么用UDP,网页解析的全过程
文章目录一、什么是DNS二、DNS的作用三、DNS作用四、DNS为什么用UDP五、如果打开一个网站很慢,要如何排查六、网页解析的全过程一、什么是DNS DNS是域名系统的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,用于TCP/IP网络。 …...
算法23:多叉树_派对的最大快乐值
公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每…...
中国ETC行业市场规模及未来发展趋势
中国ETC行业市场规模及未来发展趋势编辑根据市场调研在线网发布的2023-2029年中国ETC行业发展策略分析及战略咨询研究报告分析:随着政府坚持实施绿色出行政策,ETC行业也受到了极大的支持。根据中国智能交通协会统计,2017年中国ETC行业市场规模…...
每日刷题(一)——只出现一次的数字
前言 今天遇到一个位运算的题目,感觉很有意思,记录一下。 Question1 136. 只出现一次的数字 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实…...
洛谷P5737 【深基7.例3】闰年展示 C语言/C++
【深基7.例3】闰年展示 题目描述 输入 x,yx,yx,y,输出 [x,y][x,y][x,y] 区间中闰年个数,并在下一行输出所有闰年年份数字,使用空格隔开。 输入格式 输入两个正整数 x,yx,yx,y,以空格隔开。 输出格式 第一行输出一个正整数&a…...
shell注释
注释对于任何编程语言都是不可忽视的重要组成部分,编写者通过注释来为其他人提供解释或提示,能有效提高代码的可读性。 Bash 同其他编程语言一样提供了两种类型注释的支持。 单行注释多行注释一、Bash 单行注释 在注释段落的开头使用 # ,如下…...
【C++入门(上篇)】C++入门学习
前言: 在之前的学习中,我们已经对初阶数据结构进行相应了学习,加上之前C语言的学习功底。今天,我们将会踏上更高一级“台阶”的学习-----即C的学习!!! 文章目录1.C 简介1.1什么是C1.2.C的发展史…...
【密码学】 一篇文章讲透数字签名
【密码学】 一篇文章讲透数字签名 数字签名介绍 数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名…...
POI导入导出、EasyExcel批量导入和分页导出
文件导入导出POI、EasyExcel POI:消耗内存非常大,在线上发生过堆内存溢出OOM;在导出大数据量的记录的时候也会造成堆溢出甚至宕机,如果导入导出数据量小的话还是考虑的,下面简单介绍POI怎么使用 POI导入 首先拿到文…...
手把手教你做微信公众号
手把手教你做微信公众号 微信公众号可以通过注册的方式来建立。 1.进入微信公众平台 首先,在浏览器中搜索微信公众号,网页第一个就是,如下图所示,我们点进去。 2.注册微信平台账号 进入官网之后,如下图所示&#…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
