源码系列 之 ThreadLocal
简介
ThreadLocal的作用是做数据隔离,存储的变量只属于当前线程,相当于当前线程的局部变量,多线程环境下,不会被别的线程访问与修改。常用于存储线程私有成员变量、上下文,和用于同一线程,不同层级方法间传参等。JDK 1.8 中的 ThreadLocal 共741行代码,其中包含3个成员变量,13个成员方法和两个内部类。 我们先来看下核心原理,再来详细看下源码。
问题
我们可以带着问题去学习这部分内容,希望学习完后,能回答这些问题:
- ThreadLocal能不能代替Synchronized?和Synchronized的区别是什么?
- Thread、ThreadLocal、ThreadLocalMap的关系是怎么样的?
- 存储在jvm的堆还是栈中?
- ThreadLocal会导致内存泄漏吗,为什么?
- ThreadLocalMap为什么用Entry数组而不是Entry对象?
- ThreadLocal里的对象一定是线程安全的吗?
- ThreadLocalMap只用单纯的数组存值吗?如果出现哈希冲突怎么存值?
核心原理
这个要从java.lang.Thread类说起,每个Thread对象中都拥有一个ThreadLocalMap(ThreadLocal的内部类)的成员变量。ThreadLocalMap内部又拥有一个Entry数组,每个Entry是一个键值对,key是ThreadLocal本身,value是ThreadLocal的泛型值。 (这里Thread类虽然有ThreadLocalMap成员变量,但没有get(),set(),remove()等增删改查的方法,其实就是通过ThreadLocal来操作的。)
例如我们每一次请求,就是一个线程,然后一个线程里就有且只有一个ThreadLocalMap,然后我们的业务里可能new了好几个ThreadLocal对象,存了几个ThreadLocal的值,这些就存在Entry数组中,然后,我们根据当前线程和当前ThreadLocal就能找到唯一的<T>value值。再简单点讲:每个线程里都有一个成员变量,本质是一个数组,里面存的就是你想线程私有化的几个对象。
结构图如下:
核心源码如下:
// java.lang.Thread类里持有ThreadLocalMap的引用
public class Thread implements Runnable {... ...ThreadLocal.ThreadLocalMap threadLocals = null;... ...
}// java.lang.ThreadLocal有内部静态类ThreadLocalMap,主要提供给Thread类使用
public class ThreadLocal<T> {... ...static class ThreadLocalMap {// 存线程私有变量private Entry[] table;// ThreadLocalMap内部有Entry类,Entry的key是ThreadLocal本身,value是泛型值static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}}... ...
}
ThreadLocal的成员变量
这里介绍一下ThreadLocal的成员变量。
- private final int threadLocalHashCode = nextHashCode()
自定义的哈希值,主要是调用nextHashCode()方法获取。 - private static AtomicInteger nextHashCode = new AtomicInteger()
下一个要给出的哈希码。自动更新。从0开始。 - private static final int HASH_INCREMENT = 0x61c88647;
连续生成的哈希码之间的差异,此为一个魔数。
ThreadLocal的成员方法
这里介绍一下ThreadLocal的成员方法。
- public ThreadLocal()
构造器。
public ThreadLocal() {}
- private static int nextHashCode()
返回下一个哈希码。
private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);}
- protected T initialValue()
用以初始化值,这个方法将在线程第一次使用get方法访问变量时被调用,如果调用get()前使用了set()设置值,则不会被调用,这里只是简单的初始化成了null,如果需要初始化成其它值,则必须将ThreadLocal子类化,并重写此方法。
// 由子类提供实现。
// protected
protected T initialValue() {return null;
}
- public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
可根据提供的函数,生成初始值。
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {return new SuppliedThreadLocal<>(supplier);}
- public T get()
返回该当前线程对应的线程局部变量值。
public T get() {// 获取当前线程,这里的currentThread()是个native方法Thread t = Thread.currentThread();// 获取当前线程对应的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 若获取到了。则获取此ThreadLocalMap下的entry对象,若entry也获取到了,那么直接获取entry对应的value返回即可if (map != null) {// 获取此ThreadLocalMap下的entry对象,把当前ThreadLocal当参数传进去ThreadLocalMap.Entry e = map.getEntry(this);// 若entry也获取到了if (e != null) {@SuppressWarnings("unchecked")// 直接获取entry对应的value返回T result = (T)e.value;return result;}}// 若没获取到ThreadLocalMap或没获取到Entry,则设置初始值// 初始值方法是延迟加载return setInitialValue();
}
- boolean isPresent()
如果当前线程中ThreadLocalMap不为空,且该局部变量也不为空,则返回true,否则返回false。
boolean isPresent() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);return map != null && map.getEntry(this) != null;}
- private T setInitialValue()
设置初始值,调用initialValue。
// 设置初始值
private T setInitialValue() {// 调用初始值方法,由子类提供。T value = initialValue();// 获取当前线程Thread t = Thread.currentThread();// 获取mapThreadLocalMap map = getMap(t);// 获取到了if (map != null)// setmap.set(this, value);else// 没获取到。创建map并赋值createMap(t, value);// 返回初始值。return value;
}
- public void set(T value)
设置当前线程的线程局部变量的值。
public void set(T value) {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程对应的ThreadLocalMap实例ThreadLocalMap map = getMap(t);// 若当前线程有对应的ThreadLocalMap实例,则将当前ThreadLocal对象作为key,value做为值存到ThreadLocalMap的entry里。if (map != null)map.set(this, value);else// 若当前线程没有对应的ThreadLocalMap实例,则创建ThreadLocalMap,并将此线程与之绑定createMap(t, value);
}
- public void remove()
删除当前线程局部变量的值,目的是为了减少内存占用,和防止内存泄漏。
public void remove() {// 获取当前线程的ThreadLocalMap对象,并将其移除。ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}
- ThreadLocalMap getMap(Thread t)
获取ThreadLocalMap,在你调用ThreadLocal.get()方法的时候就会调用这个方法,它的返回是当前线程里的threadLocals的引用。
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
- void createMap(Thread t, T firstValue)
创建ThreadLocalMap,ThreadLocal底层其实就是一个map来维护的。
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}// ThreadLocalMap构造器。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);// new了一个ThreadLocalMap的内部类Entry,且将key和value传入。// key是ThreadLocal对象。table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);
}
- static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap)
用工厂方法创建继承的线程局部变量的映射。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);}
- T childValue(T parentValue)
childValue()是用来在ThreadLocal子类中定义实现的,在这里定义,主要是提供给createInheritedMap工厂方法调用。
T childValue(T parentValue) {throw new UnsupportedOperationException();}
ThreadLocal的内部类
这里介绍一下ThreadLocal的内部类。
- static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {}
ThreadLocal的扩展,从指定的提供者获取初始值。
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {private final Supplier<? extends T> supplier;SuppliedThreadLocal(Supplier<? extends T> supplier) {this.supplier = Objects.requireNonNull(supplier);}@Overrideprotected T initialValue() {return supplier.get();}}
- static class ThreadLocalMap {}
ThreadLocalMap是一个定制的散列映射,只适合维护线程本地值。ThreadLocalMap用类似HashMap的方式,存储ThreadLocal和他对应泛型的值,只不过这里只单纯的用了数组没有用到链表。没有用链表,怎么解决哈希冲突问题呢?其实很简单,依次向下一个索引查找,把值存在下一个为null的位置。
static class ThreadLocalMap {/*** ThreadLocalMap 里数组里具体存的值*/static class Entry extends WeakReference<ThreadLocal<?>> {/*** 与当前ThreadLocal 对应的值*/Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}/*** 初始容量必须是2的幂次数,当前默认为16*/private static final int INITIAL_CAPACITY = 16;/*** ThreadLocalMap 里的Entry[] 数组,长度必须为2的幂次数*/private Entry[] table;/*** 当前 Entry[] table 的长度*/private int size = 0;/*** 阈值,超过后需扩容*/private int threshold; // Default to 0/*** 设置阈值为长度的 三分之二*/private void setThreshold(int len) {threshold = len * 2 / 3;}/*** I对len取模*/private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);}/*** 获取前一个索引值*/private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1);}/*** 构造方法,懒加载的*/ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {// 根据初始容量,初始化表table = new Entry[INITIAL_CAPACITY];// 获取当前哈希值后,对len进行取模,确定索引位置int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);// 初始化长度,和阈值size = 1;setThreshold(INITIAL_CAPACITY);}/*** 从给定父映射创建新映射 * */private ThreadLocalMap(ThreadLocalMap parentMap) {// 初始化参数Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];for (Entry e : parentTable) {if (e != null) {@SuppressWarnings("unchecked")ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();if (key != null) {Object value = key.childValue(e.value);Entry c = new Entry(key, value);int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)h = nextIndex(h, len);table[h] = c;size++;}}}}/*** 查找与key相关联的条目*/private Entry getEntry(ThreadLocal<?> key) {// 确定索引值int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;elsereturn getEntryAfterMiss(key, i, e);}/*** 根据key值找不到Entry时,用以下方法找,当前i值找不到,就到i+1处找*/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)expungeStaleEntry(i);else// 当前索引 i 处找不到,就到索引 i + 1 处查找,这也是ThreadLocalMap解决哈希冲突的方法,即,当前有值,则顺位往下一个索引存i = nextIndex(i, len);e = tab[i];}return null;}/*** 根据ThreadLocal,存对应value值*/private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;// 用key的哈希值,对len取模,以计算存储的索引位置int i = key.threadLocalHashCode & (len-1);// 这几行代码就很有意思了,前面说ThreadLocalMap是没有链表的,那么怎么解决哈希冲突问题呢// 就是,依次往下一位索引存,直到有空位为止for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();// 如果key相等,则更新值if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}/*** 删除指定key的节点*/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;}}}/*** 替换已经不再被使用的旧值,(key为null的值 */private void replaceStaleEntry(ThreadLocal<?> key, Object value,int staleSlot) {Entry[] tab = table;int len = tab.length;Entry e;int slotToExpunge = staleSlot;for (int i = prevIndex(staleSlot, len);(e = tab[i]) != null;i = prevIndex(i, len))if (e.get() == null)slotToExpunge = i;for (int i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;tab[i] = tab[staleSlot];tab[staleSlot] = e;if (slotToExpunge == staleSlot)slotToExpunge = i;cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);return;}if (k == null && slotToExpunge == staleSlot)slotToExpunge = i;}tab[staleSlot].value = null;tab[staleSlot] = new Entry(key, value);if (slotToExpunge != staleSlot)cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);}/*** staleSlot和下个空槽之间的所有空槽都将被检查和清除*/private int expungeStaleEntry(int staleSlot) {Entry[] tab = table;int len = tab.length;// 清楚当前槽tab[staleSlot].value = null;tab[staleSlot] = null;size--;Entry e;int i;for (i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();// 如果发现key为空,则清除value值if (k == null) {e.value = null;tab[i] = null;size--;} else {int h = k.threadLocalHashCode & (len - 1);if (h != i) {tab[i] = null;while (tab[h] != null)h = nextIndex(h, len);tab[h] = e;}}}return i;}/*** 启发式地扫描一些单位,寻找陈旧的条目*/private boolean cleanSomeSlots(int i, int n) {boolean removed = false;Entry[] tab = table;int len = tab.length;do {i = nextIndex(i, len);Entry e = tab[i];if (e != null && e.get() == null) {n = len;removed = true;i = expungeStaleEntry(i);}} while ( (n >>>= 1) != 0);return removed;}/*** 清除旧条目后,长度依然3/4threshold,则扩容*/private void rehash() {expungeStaleEntries();if (size >= threshold - threshold / 4)resize();}/*** 将原来的容量,扩大为两倍*/private void resize() {Entry[] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2;Entry[] newTab = new Entry[newLen];int count = 0;for (Entry e : oldTab) {if (e != null) {ThreadLocal<?> k = e.get();// 清除旧表无用值if (k == null) {e.value = null; // Help the GC} else {// 找到合适位置并存储int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null)h = nextIndex(h, newLen);newTab[h] = e;count++;}}}// 更新成员变量setThreshold(newLen);size = count;table = newTab;}/*** 删除表中所有陈旧的条目*/private void expungeStaleEntries() {Entry[] tab = table;int len = tab.length;for (int j = 0; j < len; j++) {Entry e = tab[j];if (e != null && e.get() == null)expungeStaleEntry(j);}}}
问题解答
相信看了上面的源码,文章开头的部分问题,你已经有了自己的答案,接下来我们再对一下答案。
1. ThreadLocal能不能代替Synchronized?和Synchronized的区别是什么?
答:ThreadLocal肯定不能代替Synchronized,ThreadLocal只是让变量变成了完全私有化,别的线程是无法访问的。而Synchronized除了解决线程冲突外,更重要的是,可以使变量被所有线程访问和修改。
2.Thread、ThreadLocal、ThreadLocalMap的关系是怎么样的?
答:关系有点绕,Thread类中包括ThreadLocalMap成员变量,ThreadLocalMap是ThreadLocal的内部类,ThreadLocalMap有Entry数组,Entry实体是键值对,其中key即是ThreadLocal类型。
3 存储在jvm的堆还是栈中?
答:很会人会觉得变量变成线程私有了,就一定是存在虚拟机栈中的,其实不是,我们私有变量是存在Thread对象里的,而对象都是存在堆里的,所以ThreadLocal的实例和他的值都是存在堆上的。
4. ThreadLocal会导致内存泄漏吗,为什么?
答:这个要从两方面分析,即ThreadLocalMap.Entry的key和value值分别讲:key直接是交给了父类处理super(key),父类是个弱引用,所以key完全不存在内存泄漏问题,value是个强引用,如果线程终止了,也会被GC干掉,但有时线程是不会被终止的,比如线程池里的核心线程,此时引用链就变成了:Thread->ThreadLocalMap->Entry(key为null)->value
,由于value和Thread还存在链路关系,还是可达的,所以不会被回收,这样越来越多的垃圾对象产生却无法回收,最终可能导致OOM,当然解决办法也简单,用完私有变量后使用remove()方法即可,它会删除所有value值。
5. 为什么用Entry数组而不是Entry对象?
答:在同一个线程里,我们可能需要多个线程私有变量,所以需要数组。
6. ThreadLocal里的对象一定是线程安全的吗?
答:不一定,因为ThreadLocal.set()进去的对象,可能本身可能就是可供多个线程访问的,比如static对象,这样是没办法保存线程安全的。
7. ThreadLocalMap只用单纯的数组存值吗?如果出现哈希冲突怎么存值?
答:ThreadLocalMap是只用数组存Entry值,如果 i 位置出现哈希冲突,则存在 i + 1处,如果 i + 1 也不为空,则依次往下顺延,直到找到空位为止。
相关文章:
源码系列 之 ThreadLocal
简介 ThreadLocal的作用是做数据隔离,存储的变量只属于当前线程,相当于当前线程的局部变量,多线程环境下,不会被别的线程访问与修改。常用于存储线程私有成员变量、上下文,和用于同一线程,不同层级方法间传…...
C语言入门(1)——特点及关键字
1、C特点及与Java区别 1.1、C特点 面向过程 一般用于嵌入式开发、编写最底层的程序、操作系统 可以直接操作内存 可以封装动态库 不容易跨平台 有指针 可以直接操作串口 线程更加灵活 和硬件打交道速度是最快的 1.2、和Java区别 C是C的增强版,增加了一些新的特性&…...
react中useEffect和useLayoutEffect的区别
布局上 useEffect在浏览器渲染完成后执行useLayoutEffect在DOM更新后执行 特点 useLayoutEffect 总是比 useEffect 先执行;useLayoutEffect与componentDidMount、componentDidUpdate调用时机相同,都是在DOM更新后,页面渲染前调用࿱…...
NoSQL(非关系型数据库)与SQL(关系型数据库)的差别
目录 NoSQL(非关系型数据库)与SQL(关系型数据库)的差别 1.数据结构:结构化与非结构化 2.数据关联:关联性与非关联性 3.查询方式:SQL查询与非SQL查询 4.事务特性:ACID与BASE 分析ACID与BASE的含义: 5.存储方式&am…...
new bing的申请与使用教程
文章目录新必应申请新必应免代使用教程总结新必应申请 下载安装 Edge dev 版本,这个版本可以直接使用 对于没有更新的用户而言,不容易找到入口,所以我们直接使用 集成new bing的dev版本 Edge dev 下载链接:https://www.microso…...
yaml配置文件
最近在写代码,发现随着网络的增加,代码变得越来越冗余,所以就想着写一个网络的配置文件,把网络的配置放到一个文件中,而不再主函数中,这样代码开起来就好看了,调试的时候也方便了。之前写过一篇…...
284. 顶端迭代器
请你在设计一个迭代器,在集成现有迭代器拥有的 hasNext 和 next 操作的基础上,还额外支持 peek 操作。 实现 PeekingIterator 类: PeekingIterator(Iterator nums) 使用指定整数迭代器 nums 初始化迭代器。 int next() 返回数组中的下一个元…...
自学前端最容易犯的10个的错误,入门学前端快来看看
在前端学习过程中,有很多常见的误区,包括过度关注框架和库、缺乏实践、忽视算法和数据结构、忽视浏览器兼容性、缺乏团队合作经验、忽视可访问性、重构次数过多、没有关注性能、缺乏设计知识以及没有持续学习等。要避免这些误区,应该注重基础…...
【ADRC控制】使用自抗扰控制器调节起动机入口压力值
以前只知道工业控制中用的是PID控制,然而最近了解到实际生产中还在使用ADRC控制,而且使用效果还优于PID控制,遂找了几篇文献学习学习。 0 引言 自抗扰控制(Active Disturbances Rejection Controller,ADRC)…...
剑指 Offer Day2——链表(简单)
目录剑指 Offer 06. 从尾到头打印链表剑指 Offer 24. 反转链表剑指 Offer 35. 复杂链表的复制剑指 Offer 06. 从尾到头打印链表 原题链接:06. 从尾到头打印链表 最容易想到的思路就是先从头到尾打印下来,然后 reverse 一下,但这里我们使用递归…...
Final Cut Pro 10.6.5
软件介绍Final Cut Pro 10.6.5 已通过小编安装运行测试 100%可以使用。Final Cut Pro 10.6.5 破解版启用了全新的矩形图标,与最新的macOS Ventura设计风格统一,支持最新的macOS 13 文图拉系统,支持Apple M1/M2芯片。经过完整而彻底的重新设计…...
Modelsim仿真操作指导
目录 一、前言 二、仿真分类 三、RTL级仿真 3.1创建库 3.2 仿真配置设置 3.3 运行仿真 四、常见问题 4.1 运行仿真时报错“cant read "Startup(-L)": no such element in array” 4.2 运行仿真时无任何报错,但object窗口为空,可正常运…...
你知道这20个数组方法是怎么实现的吗?
前言你们一定对JavaScript中的数组很熟悉,我们每天都会用到它的各种方法,比如push、pop、forEach、map……等等。但是仅仅使用它就足够了吗?如此出色,您一定不想停在这里。我想和你一起挑战实现20数组方法的功能。1、forEachforEa…...
《系统架构设计》-01-架构和架构师概述
文章目录1. 架构的基本定义1.1 架构组成理论1.1.1 系统元素1)概念2)静态结构和动态结构1.1.2 基本系统属性1.1.3 设计和发展原则1.2 架构的决策理论1.2.1 统一软件过程(Rational Unified Process,统一软件过程)1.2.2 决…...
第七届蓝桥杯省赛——5分小组
题目:9名运动员参加比赛,需要分3组进行预赛。有哪些分组的方案呢?我们标记运动员为 A,B,C,... I下面的程序列出了所有的分组方法。该程序的正常输出为:ABC DEF GHIABC DEG FHIABC DEH FGIABC DEI FGHABC DFG EHIABC DFH EGIABC DF…...
中国专科医院行业市场规模及未来发展趋势
中国专科医院行业市场规模及未来发展趋势中国专科医院行业在过去几年中取得了跨越式发展,市场规模不断扩大,未来的发展前景也远比过去更加乐观。根据市场调研在线网发布的2023-2029年中国专科医院行业运营现状及发展前景预测报告分析,截至2018年…...
【刷题笔记】--两数之和Ⅳ,从二叉树中找出两数之和
法一:深度搜索中序遍历双指针 思路:通过中序遍历二叉树得到一个递增的数列,再在这个递增的二叉树中找到这两数。 主要学到双指针这个方法。 对于一般数列,我们要找到两数满足其之和等于目标数,我们一般会进行暴力&a…...
浏览器渲染原理JavaScript V8引擎
浏览器渲染原理 前言 在我们面试过程中,面试官经常会问到这么一个问题,那就是从在浏览器地址栏中输入URL到页面显示,浏览器到底发生了什么? 浏览器内有哪些进程,这些进程都有些什么作用;浏览器地址输入U…...
在TheSandbox 的「BOYS PLANET」元宇宙中与你的男孩们见面吧!
世界各的男孩们成为 K-Pop 男团的旅程。 Mnet 的全球项目 BOYS PLANET 终于在 2 月 2 日首次亮相! The Sandbox 与 CJ ENM 合作,于 2 月 6 日晚上 10 点开始举办两个基于 BOYS PLANET 生存节目的虚拟体验:BOYS PLANET:BOYS LAND 和…...
数据结构与算法:java对象的比较
1.基本类型的比较 在Java中,基本类型的对象可以直接比较大小。 public class TestCompare {public static void main(String[] args) {int a 10;int b 20;System.out.println(a > b);System.out.println(a < b);System.out.println(a b);char c1 A;char…...
python(16)--类
一、类的基本操作1.定义一个类格式:class Classname( ):内容💎鄙人目前还是一名学生,最熟悉的也就是学校了,所以就以学校为例子来建立一个类吧class School():headline"帝国理工大学"def schoolmotto(self):…...
CNI 网络流量分析(七)Calico 介绍与原理(二)
文章目录CNI 网络流量分析(七)Calico 介绍与原理(二)CNIIPAM指定 IP指定非 IPAM IPCNI 网络流量分析(七)Calico 介绍与原理(二) CNI 支持多种 datapath,默认是 linuxDa…...
API安全的最大威胁:三体攻击
最近《三体》火的一塌糊涂,动画片、电视剧和书都受到了大家的喜爱。在API安全上,最近也发现了三体攻击。 当然了,肯定是不来自于三体人的攻击,这里的三体攻击指的是(trinity,也称三位一体攻击),是一个新的攻击手法。具体的情况老李也找到了相关的介绍,下面就分享给大…...
分布式事务解决方案——TCC
TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。1、Try 阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm一起才能真正构成…...
ITSS认证分为几个级别,哪个级别最高
一、什么是ITSS ITSS( 信息技术服务标准,简称ITSS)是国内第一套成体系和综合配套的信息技术服务标准库,全面规范了IT服务产品及其组成要素,用于指导实施标准化和可信赖的IT服务。 ITSS是在工业和信息化部、国家标准化管理委员会的联合指导下…...
ZigBee案例笔记 - USART
文章目录1.串行通信接口简述2.串行通信接口寄存器U0CSR (0x86) -USART 0 控制和状态U0UCR (0xC4)–USART 0 UART 控制U0GCR (0xC5)–USART 0 通用控制U0BUF (0xC1) – USART 0 接收/传送数据缓存U0BAUD (0xC2) – USART 0 波特率控制3.设置串行通信接口比特率控制寄存器4.外设I…...
java | 基于Redis的分布式锁实现①
前言 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性。在任意时刻,只有一个客户端能持有锁。不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户…...
十六、基于FPGA的CRC校验设计实现
1,CRC校验循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的…...
2022爱分析 · DataOps厂商全景报告 | 爱分析报告
报告编委 李喆 爱分析合伙人&首席分析师 廖耘加 爱分析分析师 目录 1. 研究范围定义 2. 市场洞察 3. 厂商全景地图 4. 市场分析与厂商评估 5. 入选厂商列表 1. 研究范围定义 研究范围 在后疫情时代,以数据分析为代表的数据消费场景日益丰富&…...
京东前端react面试题及答案
useEffect 与 useLayoutEffect 的区别 (1)共同点 运用效果: useEffect 与 useLayoutEffect 两者都是用于处理副作用,这些副作用包括改变 DOM、设置订阅、操作定时器等。在函数组件内部操作副作用是不被允许的,所以需…...
新开传奇网站999新服网/网站搭建策略与方法
Proactor IO即,在读写IO完成后的通知。 Reactor模式,采用的非阻塞模式,但资源的读写操作仍然是同步的。 而Proactor模式在读写操作上是异步完成的,资源读写靠操作系统完成,而不是应用进程。操作系统完成读写后&#x…...
网站开发源代码什么意思/培训心得体会感悟
提起物联网(IoT)和人工智能(AI),人们并不陌生。作为当今时代十分热门的科技概念,它们其实都与「数据」有关:IoT 解决了数据从哪里来,AI 则解决了数据去往何方、用于何处。一个将两者…...
网站百度没收录/seo全称是什么意思
bug描述: https://jira.qos.ch/browse/LOGBACK-1175 commits: https://github.com/qos-ch/logback/commit/f264607fb450 但是!!!这个bug在1.3.0-alpha1中才修复,最新的released版本1.2.3中,这个bug依旧存…...
服装网站的建设策划/推广关键词外包
目录 前言篇 内存参数篇 jstack-栈信息 jmat-堆信息 jstat-GC信息 前言篇 实测:分别调整JVM堆大小,启动idea,jstat -GC 查看堆信息如下: 64bit-16G 电脑 S0C(kb) S1C(kb) EC(kb) OC(kb) MC(kb) 年轻代 老年代 堆大小 年轻代占比 老年代占比 年轻代Eden占比 年轻代S…...
有哪些招聘网站/郑州seo地址
2019独角兽企业重金招聘Python工程师标准>>> 忙了几天,今天在公司居然没什么活干 ,所以早上就用公司的电脑写写之前在公司编写framebuffer的使用心得体会总结,这也算是一点开发经验,不过我还没写全,精华部分…...
上海黑马网站制作/网站seo课程
#####找规律:以“$”为红点,连线起来即为下图,不难得出总行数总列数均为t4n5(正方形),总行数列数一定为奇数,很容易确定最中间的点为a[t/2][t/2]。![](/image_editor_upload/20200215044635_79242.jpg)#####由于n为正整…...