什么是ThreadLocal ThreadLocal用于储存专属于某个线程变量的值(线程私有)。同一个ThreadLocal变量,在不同线程下读取到的变量值是不同的,可以做到变量在线程之间的隔离。和传统方式定义的变量不同,传统方式的成员变量是多个线程共享的。 ThreadLocal的使用方法 定义ThreadLocal变量 ThreadLocal最好使用static类型声明。具体原因在后面源代码分析中解释。 Java 8之前使用下面的方式定义ThreadLocal并指定初始值 private static ThreadLocal local = new ThreadLocal() { @Override protected Integer initialValue() { return 0; } }; 在java 8 之后推荐使用如下的方式: private static final ThreadLocal local = ThreadLocal.withInitial(() -> 0); 注意:必须使用以上两种方式之一来指定初始值。举一个反例,比如下面的代码: static ThreadLocal local = new ThreadLocal<>(); static Object o = new Object(); public static void someMethod() { if (null == local.get()) { local.set(o); } } 这段逻辑看似为ThreadLocal指定了默认值。但是实际运行时,每个线程持有的ThreadLocal的值都是同一个Object。不同线程之间的变量仍然是共用的,没有线程隔离。一定不要这样使用ThreadLocal。 读写ThreadLocal变量 这里比较简单,直接代码说明。 // 写入变量值 local.set(2); // 读取变量值 Integer i = local.get(); 清除ThreadLocal变量值 线程不再使用ThreadLocal变量,需要调用remove方法,否则会发生内存泄漏。 local.remove() ThreadLocal的原理 我们从get读取变量值这个方法入手分析。 get方法代码如下: public T get() { // 获取当前Thread Thread t = Thread.currentThread(); // 获取ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { // 如果map存在 // 获取该ThreadLocal对应的MapEntry ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") // 如果该entry存在,返回entry对应的value T result = (T)e.value; return result; } } // 否则,执行设置初始值的逻辑 return setInitialValue(); } 由以上代码可知ThreadLocalMap是变量值的载体。我们看一下getMap方法: ThreadLocalMap getMap(Thread t) { return t.threadLocals; } 返回的是Thread类里面threadLocals的值。 这里的关系可能比较复杂。ThreadLocalMap在Thread类中保存。ThreadLocal变量获取值的时候,先获取当前线程的ThreadLocalMap,在从这个ThreadLocalMap中获取key为调用get方法的ThreadLocal变量所对应的value。我们可以得出如下结论: ThreadLocal变量的值分别在各个Thread中保存。 同一个Thread的ThreadLocalMap保存了该Thread在多处ThreadLocal变量中的对应的值。 ThreadLocalMap是ThreadLocal的一个静态内部类。和java.util.Map类似,ThreadLocalMap也拥有Entry。如下所示: static class Entry extends WeakReference> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } } 每一个Entry对象保存了一个ThreadLocal和value的对应关系。 这里有一个最特殊的地方,Entry继承了WeakReference。说到WeakReference自然需要提到StrongReference。但Java并没有StrongReference这个类。我们平时写的代码,例如: Object o = new Object(); 这里的o就是一个Strong Reference,即强引用。无论什么时候,只要对象存在强引用,GC时候都不会被回收。 但是WeakReference则不同,如果一个对象仅仅被WeakReference引用,那么GC的时候,该对象会被回收。一个例子如下: public class WeakRefDemo { public static void main(String[] args) { WeakDemo weakDemo = new WeakDemo(); System.out.println(weakDemo.strongReference); System.out.println(weakDemo.weakReference.get()); // (1) Object o = weakDemo.weakReference.get(); System.gc(); System.out.println(weakDemo.strongReference); System.out.println(weakDemo.weakReference.get()); } } class WeakDemo { Object strongReference = new Object(); WeakReference weakReference = new WeakReference<>(new Object()); } 为了对比结果,WeakDemo类中同时定义了强引用和弱引用。 保持(1)处注释不动,运行代码,会得到类似如下输出: java.lang.Object@1b6d3586 java.lang.Object@4554617c java.lang.Object@1b6d3586 null 我们发现GC过后,strongReference依然可访问,然而weakReference已经被回收,值变成了null。 如果取消(1)这一行的注释,再次执行代码,会得到类似如下的输出: java.lang.Object@1b6d3586 java.lang.Object@4554617c java.lang.Object@1b6d3586 java.lang.Object@4554617c 和上一次不同,这次在GC之前weakReference引用的对象在别处存在强引用,因此它不再被GC回收。 我们回到ThreadLocalMap的Entry类。Entry是一个指向ThreadLocal的WeakReference。而定义ThreadLocal的对象会持有对ThreadLocal的强引用。如果Entry指向ThreadLocal不使用WeakReference,即便是定义了ThreadLocal的对象不再使用,只要线程不销毁,还是能够通过Thread -> ThreadLocalMap -> Entry -> ThreadLocal -> 定义ThreadLocal的对象这条引用链追溯到,因此会有严重的内存泄漏问题。 一开始提到ThreadLocal最好使用static变量类型。因为static修饰符避免了不同的实例创建出不同的ThreadLocal变量。虽然不添加static修饰也不影响使用,但是会造成变量浪费。ThreadLocal变量真正的内容不是在ThreadLocal中存储,而是在各个线程自己的ThreadLocalMap中。所以说建议使用static修饰ThreadLocal变量。 我们继续分析ThreadLocal的get方法。如果Thread的ThreadLocalMap为null,或者是线程的ThreadLocalMap中不存在key为这个ThreadLocal变量的entry,会执行设置初始值的操作。方法代码如下所示: private T setInitialValue() { // 这里调用的是使用方法里介绍的,设置ThreadLocal初始值的方法 // 设定初始值需要继承ThreadLocal类,并覆盖这个方法 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) // 如果map存在,在map中设置ThreadLocal和value的映射关系 // 当然这个方法还隐藏了其他逻辑,后面分析 map.set(this, value); else // 如果map为null,为线程创建一个ThreadLocalMap // 并创建一个entry,保存当前ThreadLocal和value的对应关系 createMap(t, value); return value; } 我们看一下ThreadLocalMap的set方法(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; // 获取ThreadLocalMap的容量,初始值为16 int len = tab.length; // 每一个ThreadLocal变量都有一个独一无二的hashCode // 该值与map容量减一按位与之后,得到的值换算为10进制,作为该ThreadLocal变量值在map中对应entry的下标存储 // 无论map怎么扩容,内部table的length总是2的n次方数,减去1之后可以获取到一个每一位全是1的二进制数 // ThreadLocal和这个数按位与之后可以在table中分布的更为平均,尽量避免hash碰撞 int i = key.threadLocalHashCode & (len-1); // nextIndex获取的是下一个index(++index),如果++index越界,返回0,数组从头开始 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); // 如果k是当前的ThreadLocal变量,说明找到了当前ThreadLocal对应的entry,更新它的value并返回 if (k == key) { e.value = value; return; } // 如果k为null,说明之前这个entry对应的ThreadLocal变量已经被回收 // key已经被回收的entry在源代码中称为stale entry // 这个stale entry会被替换掉 if (k == null) { replaceStaleEntry(key, value, i); return; } // 如果这两个if都没有执行,说明可能存在hash碰撞,即其他ThreadLocal的hashcode运算之后的下标和当前ThreadLocal运算的结果一致 // 并且其他ThreadLocal的变量已经在map中储存 // 这时候尝试继续寻找key对应的entry // 也可能是key对应的entry没有创建 // 这种情况会一直到for循环执行完毕,在下面步骤创建出新的entry } // 如果下标对应的entry不存在,创建一个新的 tab[i] = new Entry(key, value); // map的大小加一 int sz = ++size; // 查找当前index之后,以2为底sz的对数个entry,如果有stale entry,清除他们,具体稍后分析 // 这里之所以没有扫描所有的stale entry,是为了平衡清除stale entry操作和时间的消耗 // 如果没有发现stale entry,判断sz是否大于阈值 // 阈值为map容量的三分之二 // 如果超过了阈值,清除所有的stale entry,并且再次判断是否需要扩容 // rehash流程稍后分析 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } 接下来分析下replaceStaleEntry的代码。该方法参数中存放stale entry的index称为staleSlot。 private void replaceStaleEntry(ThreadLocal key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; // Back up to check for prior stale entry in current run. // We clean out whole runs at a time to avoid continual // incremental rehashing due to garbage collector freeing // up refs in bunches (i.e., whenever the collector runs). int slotToExpunge = staleSlot; // 从staleSlot位置向前查找其他stale slot,直到发现前面entry的slot不存在为止 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; // Find either the key or trailing null slot of run, whichever // occurs first // 从stale位置的下一个index开始循环 for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // If we find key, then we need to swap it // with the stale entry to maintain hash table order. // The newly stale slot, or any other stale slot // encountered above it, can then be sent to expungeStaleEntry // to remove or rehash all of the other entries in run. // 如果找到了一个entry,key是当前的ThreadLocal if (k == key) { // 替换value为新的值 e.value = value; // 和stale entry交换位置 // 因为当前ThreadLocal对应的entry的位置(ThreadLocal的hashCode决定,之前已分析)本来就应该是stale entry的位置 tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists // 如果slotToExpunge值没有改变,那么就从下标i开始清理stale entry // 因为i处的entry已经被交换为stale entry if (slotToExpunge == staleSlot) slotToExpunge = i; // expungeStaleEntry清理stale slot到下一个null slot之前所有的stale entry(左闭右开区间) // 返回值是下一个null slot(下标) // 再扫描下一个null slot(下标)往后以2为底len的对数个slot内所有的stale entry并清除 // cleanSomeSlots还包含其他行为,后续分析 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } // If we didn't find stale entry on backward scan, the // first stale entry seen while scanning for key is the // first still present in the run. // 如果在前面的for循环(倒着查找stale entry)没有发现其他的stale entry // 并且当前index正好是stale slot // 设置slotToExpunge为当前index // 因为该if进入之后slotToExpunge会被修改,之后不会再次进入 // 所以说这里设置slotToExpunge为staleSlot到下一个null slot之间(开区间)的第一个stale slot if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // If key not found, put new entry in stale slot // 如果没有发现key为当前ThreadLocal的entry,创建一个新的entry tab[staleSlot].value = null tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them // 如果有其他的stale entry,运行清理方法 if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } 经过分析可知replaceStaleEntry从staleSlot位置向前查找stale entry,直到遇到null slot为止。比如: stale | null | stale | entry | stale | stale | entry A B C D E F G map中存储有上面所示的多个entry。执行replaceStaleEntry传入的是F位置。那么根据以上逻辑,slotToExpunge最终会指向C。 接下来replaceStaleEntry会从C位置开始清除stale slot。 我们分析下清理stale entry的逻辑。位于expungeStaleEntry方法。代码如下所示: private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot // 设置stale entry的value为null,释放掉value的引用 tab[staleSlot].value = null; // 释放掉stale entry的引用 tab[staleSlot] = null; // 减小size size--; // Rehash until we encounter null Entry e; int i; // 从stale slot下一个index开始循环,直到entry为null为止 for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); if (k == null) { // 如果发现key为null,说明又发现了一个stale entry,执行数据擦除 e.value = null; tab[i] = null; size--; } else { // 根据key对应的hash,计算它的slot位置(下标) int h = k.threadLocalHashCode & (len - 1); // 如果key不在它本应该属于的下标位置 if (h != i) { // 清除下标i对应的entry // 重新计算安排entry的位置 tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. // 从h位置向后查找,直到发现空位置 // 把现在现在遍历到的这个entry放置在这个空位置 while (tab[h] != null) h = nextIndex(h, len); tab[h] = e; } } } // 返回方法参数中staleSlot后面第一个空slot的下标 return i; } 由以上分析可知expungeStaleEntry不仅清理了staleSlot下标对应的entry,还顺便清理了staleSlot到下一个null slot之间的所有stale slot。除此之外还重新分配上述区间内实际存储下标和从hashCode计算出的下标不一致的entry的位置。 接下来需要分析cleanSomeSlots方法。该方法从参数i这个下标开始(不包括i),向后查找以2为底n的对数个slot,如果中间发现有stale slot,调用expungeStaleEntry方法清除。同时重置n为map的容量(即需要再扫描至少log2(容量)-1个entry)。 正如英文注释所说,该方法之所以没有一开始就遍历整个map去清除stale entry,是因为需要从性能方面考虑作出权衡。 private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; // 获取map的容量 int len = tab.length; do { i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { // 如果e为stale entry // 重置n为map容量 n = len; // 设置removed标记为true removed = true; // 清除下标i位置对应的entry i = expungeStaleEntry(i); } // n每次循环都除以2 } while ( (n >>>= 1) != 0); // 如果有entry被擦除,返回true return removed; } 还剩下一个rehash方法。rehash方法负责清理map中所有的stale entry。如果清理过后map的已用空间还是过大(超过阈值的四分之三),会进行扩容操作。 rehash方法代码如下: private void rehash() { // 清理所有的stale slot expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis // 如果已用空间仍然大于等于阈值的四分之三,执行扩容操作 if (size >= threshold - threshold / 4) resize(); } expungeStaleEntries代码如下所示: 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); } } 该方法遍历所有的entry,清理stale entry。 最后是负责扩容,重新计算slot位置的resize方法代码: private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; // 扩容为原来的2倍 int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; // 遍历老的map entry for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal k = e.get(); if (k == null) { // 如果是stale entry,清理掉他的value e.value = null; // Help the GC } else { // 根据hashCode和新的length计算出entry所属的slot(下标) int h = k.threadLocalHashCode & (newLen - 1); // 如果计算出来的slot被占用(发生hash碰撞),逐个向后找到一个空闲的slot while (newTab[h] != null) h = nextIndex(h, newLen); // 放置entry到空闲slot newTab[h] = e; // 计数加一 count++; } } } // 重新设置新的阈值 setThreshold(newLen); // 设置新的size和table size = count; table = newTab; } Java8设置ThreadLocal初始值的逻辑 在Java8之后不推荐使用继承ThreadLocal重写initialValue方法的方式来指定初始值。 Java8建议使用withInitial静态方法,提供一个Supplier方法(无参数有返回值)作为默认值生成器。 withInitial方法代码如下所示: public static ThreadLocal withInitial(Supplier supplier) { return new SuppliedThreadLocal<>(supplier); } 方法返回了一个SuppliedThreadLocal类型。我们查看下它的代码: static final class SuppliedThreadLocal extends ThreadLocal { private final Supplier supplier; SuppliedThreadLocal(Supplier supplier) { this.supplier = Objects.requireNonNull(supplier); } @Override protected T initialValue() { return supplier.get(); } } 该类继承了ThreadLocal。重写的initialValue方法调用supplier的get方法并返回。实际上和JDK8之前的使用方式没有区别,只不过Java帮我们做了一层封装,可以用更为优雅的方式指定初始值。 ThreadLocal内存泄漏问题 文章开始的时候介绍entry继承了WeakReference。这样使用的目的是为了帮助GC回收ThreadLocal变量所在对象。这是因为Thread -> ThreadLocalMap -> Entry -> ThreadLocal -> ThreadLocal变量所在对象这一条引用链中Entry -> ThreadLocal这一环是弱引用。尽管如此,如果不恰当使用ThreadLocal,内存泄漏问题依然会存在,因为entry对象本身并不会因为弱引用的缘故自动回收。 按照JDK文档,线程不再使用ThreadLocal变量的时候,需要调用remove方法,清除对应的entry释放内存。避免形成内存泄漏。 remove方法。该方法间接调用了ThreadLocalMap的remove方法。如下所示: public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } ThreadLocalMap的remove方法和解释如下所示: private void remove(ThreadLocal key) { Entry[] tab = table; int len = tab.length; // 计算key对应的下标 int i = key.threadLocalHashCode & (len-1); // 从下标i处向后逐个遍历tab中的entry,直到遇到null entry for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { // 如果找到需要清理的entry,清理它的引用 e.clear(); // 清理这个stale entry expungeStaleEntry(i); // 最后返回 return; } } } 本文为原创内容,欢迎大家讨论、批评指正与转载。转载时请注明出处。 你可能感兴趣的:(ThreadLocal原理解析) LangChain 发布政策详解 VYSAHF langchain人工智能深度学习python 技术背景介绍LangChain是一个用于构建和部署大型语言模型(LLM)应用的生态系统。它由多个组件包组成,例如langchain-core、langchain、langchain-community、langgraph和langserve等。随着应用需求的快速变化,LangChain的开发与发布策略也相应调整,以便更好地服务于用户社区。核心原理解析LangChain生态系统采用语义版本控制(Se 在LangChain中运行Replicate模型的实用指南 fgayif langchain人工智能python ##技术背景介绍Replicate是一个平台,可以轻松调用各种预训练的AI模型。与传统的模型托管和调用相比,Replicate提供了简单的API接口,使开发者能够快速集成和使用强大的AI模型。本文将重点介绍如何在LangChain项目中集成和调用Replicate模型。##核心原理解析在集成Replicate模型之前,需要进行一些基础设置和安装工作。LangChain是一个用于自然语言处理的库,它 使用 OpenAI Functions Tool Retrieval Agent 实现智能工具选择 bavDHAUO 服务器java前端python 技术背景介绍在构建智能代理系统时,我们通常需要调用多个工具来满足不同的用户查询。然而,随着工具数量的增加,将所有工具描述信息放入每次的查询上下文中变得不切实际。为了解决这个问题,我们引入了工具检索(ToolRetrieval)的概念。通过动态选择与查询相关的工具,我们不仅能节省上下文空间,还能提高代理的响应效率。核心原理解析工具检索的核心思想是在运行时根据用户的输入动态选择要使用的工具。我们将展示 vue scoped 原理解析 短暂又灿烂的 前端vuevue.jsjavascript前端 Vuescoped,原理,涉及到vue-loader的处理策略:一、首先呢,是VueLoaderPlugin策略:VueLoaderPlugin先获取了webpack原来的rules(即compiler.option.module.rule的比如test:/.vue$/规则),然后创建了pitcher规则,pitcher中的pitcher-loader可以通过resourceQuery识别引入文件 快速入门OpenAI聊天模型的实战指南 shuoac python #快速入门OpenAI聊天模型的实战指南OpenAI的聊天模型在开发人工智能应用时至关重要。本文将详细介绍如何使用OpenAI的聊天模型进行开发,并提供可运行的代码示例。##技术背景介绍OpenAI提供了多种聊天模型,支持不同的输入类型和功能,如工具调用、结构化输出等。通过Azure平台,也可以访问OpenAI模型,适合需要云集成的场景。##核心原理解析聊天模型利用自然语言处理技术生成响应,支持不 ThreadLocal内存泄漏 PinkandWhite complex&instrument面试&面试题javathread弱引用 ThreadLocal内存泄漏实线代表强引用,虚线代表弱引用每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本。强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收站回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。一般情况下,如果想取消强引用和 使用LangChain构建大语言模型(LLM)应用程序: 基础入门 bavDHAUO langchain语言模型服务器python 使用LangChain构建大语言模型(LLM)应用程序:基础入门在这个教程中,我们将了解如何使用LangChain快速上手并构建一个基本的大语言模型(LLM)应用。我们将涵盖核心概念、基本实现和应用场景分析。技术背景介绍LangChain是一个强大的框架,旨在帮助开发人员构建涉及LLM的应用程序。从简单的聊天机器人到复杂的数据分析系统,LangChain提供了一整套工具来实现这一目标。核心原理解析 多线程设计模式-本地线程(Threadlocal)使用详解以及原理介绍 睡醒的土豆 java多线程java并发编程多线程设计模式多线程并发编程thread 本地线程(Threadlocal)什么是ThreadLocal变量ThreadLoal变量,线程局部变量,同一个ThreadLocal所包含的对象,在不同的Thread中有不同的副本。这里有几点需要注意:因为每个Thread内有自己的实例副本,且该副本只能由当前Thread使用。这是也是ThreadLocal命名的由来。既然每个Thread有自己的实例副本,且其它Thread不可访问,那就不存在多 调用链追踪(Trace ID) 18你磊哥 java 前言:在Java中实现调用链追踪(TraceID)通常用于分布式系统中跟踪请求的完整链路,常见的实现方式包括手动编码或使用开源框架(如SkyWalking、Zipkin、SpringCloudSleuth等)。以下是具体实现方法及示例:1.手动实现TraceID通过ThreadLocal或MDC(MappedDiagnosticContext)存储TraceID,并在请求链路中传递。步骤1:定义T 【大模型技术】LlamaFactory 的原理解析与应用 大数据追光猿 大模型transformer人工智能语言模型pythongithubdocker机器学习 LlamaFactory是一个基于LLaMA系列模型(如LLaMA、LLaMA2、Vicuna等)的开源框架,旨在帮助开发者和研究人员快速实现大语言模型(LLM,LargeLanguageModel)的微调、推理和部署。它提供了一套完整的工具链,支持从数据准备到模型训练、优化和应用的全流程开发。以下是关于LlamaFactory的解析:1.LlamaFactory的核心功能(1)模型微调支持多种微 ThreadLocal的使用与原理解析 Rolland_hero JUC学习以及源码分析juc 目录基本介绍使用方法实际案例ThreadLocal的实现原理结构介绍ThreadLocal的核心方法源码set方法get方法remove方法ThreadLocal的内存泄露问题ThreadLocalMap扩容问题基本介绍从Java官方文档中的描述:ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的 ThreadLocal解析 八股文领域大手子 数据库javasqljvm 1.ThreadLocal的定义与核心作用ThreadLocal是Java中用于实现线程局部变量的工具类。它为每个线程提供独立的变量副本,使得每个线程访问的是自己的数据,从而避免多线程环境下的资源共享问题,实现线程隔离。例如,解决SimpleDateFormat的非线程安全问题:每个线程通过ThreadLocal持有自己的实例,避免并发修改。2.使用场景线程安全资源管理:如数据库连接(Connec Eureka Server 原理解析 倚楼听风语 eurekaspringcloud云原生 EurekaServer同时也是一个EurekaClient,在不禁止EurekaServer的客户端行为时,它会向配置文件中的其他EurekaServer进行拉取注册表、服务注册和发送心跳等操作。作为服务注册中心,EurekaServer提供了以下功能。一、服务注册接口:/eureka/apps/SEVER_NAME源码如下://com.netflix.eureka.resources.Appl Eureka Server 数据同步原理解析 CT随 eureka云原生 EurekaServer数据同步原理解析引言在分布式系统中,服务注册与发现是微服务架构的核心组件之一。Eureka作为Netflix开源的服务注册与发现工具,被广泛应用于SpringCloud生态系统中。本文将深入探讨EurekaServer之间的数据同步原理,帮助开发者理解其工作机制。1.EurekaServer的角色EurekaServer是一个高可用的服务注册中心,负责管理所有服务实例的注册 JVM常用概念之对象初始化的成本 剑海风云 JDK(JavaDevelopmentKit)jvmjava对象初始化的成本 在JVM常用概念之新对象实例化博客中我讲到了对象的实例化,主要包含分配(TLAB)、系统初始化、用户初始化,而我在JVM常用概念之线程本地分配缓冲区(ThreadLocalAllocationBuffer,TLAB)博客中也讲到TLAB分配的效率是非常高的,而系统初始化和用户初始化是可以进行合并的,那最后就剩下将数据写入内存这部分的成本没有讨论过,那对于对象初始化而言,写入内存这部分的成本是怎么样 数据整合平台Airbyte中的Shopify连接器使用指南 bavDHAUO python 技术背景介绍Airbyte是一种专门用于ELT数据集成的平台,支持从API、数据库和文件到数据仓库和数据湖的管道搭建。其拥有最大规模的ELT连接器目录,支持众多的数据仓库和数据库。本文将介绍如何使用Airbyte的Shopify连接器加载Shopify对象作为文档。核心原理解析Airbyte的Shopify连接器作为一个文档加载器,通过API将Shopify的订单、产品等对象加载为文档。用户可以通 使用 Microsoft OneDrive 加载文档的指南 shuoac microsoftonedrivepython 技术背景介绍MicrosoftOneDrive(以前称为SkyDrive)是由微软运营的文件托管服务。通过OneDrive,你可以在云端存储和共享文档、照片、视频等数据。本文将介绍如何从OneDrive加载文档,目前支持的文件格式包括docx、doc和pdf。核心原理解析为了能够从OneDrive加载文档,需要进行以下几个步骤:注册应用程序以获取客户端ID和密钥。获取OneDrive的DriveI 使用LangChain与GPT4All模型进行交互 bavDHAUO langchain交互python 技术背景介绍近年来,开源模型和框架在AI技术领域迅猛发展。GPT4All是一个开源的对话机器人生态系统,旨在为用户提供干净的助手数据,包括代码、故事和对话。这篇文章将介绍如何使用LangChain与GPT4All模型进行交互,以实现智能问答功能。核心原理解析GPT4All是基于大型语言模型(LLMs)的开源项目,通过训练大量干净的数据,能够生成高质量的对话和回答。LangChain是一种用于简化与 使用GitPython和GitLoader进行版本控制与文档加载 vaidfl python 技术背景介绍Git是一种分布式版本控制系统,用于跟踪文件集的更改,通常用于程序员协作开发软件源代码。Git的特点包括支持分支和合并、轻量级、快速操作以及强大的社区支持等。在Python开发中,我们可以使用GitPython库来操作和管理Git仓库。此外,借助GitLoader,我们可以轻松地从Git仓库加载文档,以便在各种应用中使用。核心原理解析GitPython是一个Python库,它允许你通过 使用Google Cloud Vertex AI构建RAG匹配引擎 vaidfl python 技术背景介绍RAG(Retrieval-AugmentedGeneration)是一种结合信息检索和生成技术的框架。在GoogleCloudPlatform的VertexAI中,我们可以利用MatchingEngine来快速高效地从大规模的数据集中检索相关文档或上下文。利用预先创建的索引,RAG能够根据用户提供的问题检索到最有用的信息,并辅助生成更精确的回答。核心原理解析RAG匹配引擎在Verte html+css 实现发光倒影按钮 宝码香车 #html+css+js绚丽按钮htmlcss前端 效果html+css鼠标悬停,发光倒影按钮原理解析通过a元素的hover效果实现的过渡动画:给a元素定义一个初始值,当hover时,改变这个值。这里是给a元素定义一个css3的过渡动画。当hover时改变这个过渡动画的参数。 使用Momento Vector Index (MVI) 高效管理和查询数据 bavDHAUO python人工智能机器学习 MomentoVectorIndex(MVI)技术背景介绍在数据管理和查询中,无服务器架构的MomentoVectorIndex(MVI)提供了一种高效便捷的解决方案。MVI允许用户轻松进行数据索引和查询,无需担心基础设施和扩展问题。通过MVI,用户可以快速对数据执行向量化处理,并通过自动扩展功能满足不同规模的数据需求。核心原理解析MVI利用向量索引技术,通过将文本数据转换为向量形式,来实现高效的 使用 Airbyte 数据集成平台加载 Shopify 数据 fgayif python 技术背景介绍Airbyte是一个用于API、数据库及文件到仓库和数据湖的ELT管道的数据集成平台。它拥有最全的ELT连接器目录,为各种数据源提供强大支持。本文将介绍如何通过Airbyte平台的Shopify连接器加载数据。核心原理解析Airbyte的Shopify连接器将Shopify数据的各种对象作为文档加载,并将其存储为元数据。通过这种方式,可以方便地将Shopify的订单等信息整合到数据仓库 spring security面试题 「已注销」 springjava后端 1、springsecurity所谓的全局上下文是如何实现的?ThreadLocal2、了解springsecurity哪些核心组件,并介绍?AuthenticationManagerBuilder@Configuration@EnableWebSecuritypublicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{@Over 2024腾讯最新面经总结:面试题库+实战笔记(附答案解析) 2401_84048521 程序员面试笔记职场和发展 20、你们用过缓存吗没有,但是用redis做了分布式锁21、你说说下分布式锁怎么做的?分布式锁也是一个锁,需要满足几个特性,1可重入2可以识别加锁的身份防止ABA问题3考虑是否需要续约key是所需要加上的锁的业务资源唯一编码,value是当前线程的uuid,uuid存在threadLocal内加锁的时候用的jedis,先设一个过期时间,然后用ex,若不存在key则添加新key,若已经存在则直接失败 【SpringBoot】——如何在Spring Boot中使用ThreadLocal来存储和获取用户详情信息以及实体类参数验证 Y小夜 Springbootspringboot后端java 个人主页:【Y小夜】作者简介:一位双非学校的大二学生,编程爱好者,专注于基础和实战分享,欢迎私信咨询!入门专栏:【MySQL,Java基础,Rust】热门专栏:【Python,Javaweb,Vue框架】感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️目录ThreadLocal如何在SpringBoot中使用ThreadLocal来存储和获取用户详情信息创建用户详情类使用拦截器或过滤器 论文解读(全头皮重建方向):3DCMM FLOWVERSE 3d3D人头补全 从面部到完整头部:3DCMM的技术原理解析引言在计算机图形学和人体工学领域,3D头部模型的需求日益增加。无论是虚拟化身的创建还是头盔的个性化设计,仅有面部模型往往不足以满足要求,完整的头部几何(包括头皮)才是关键。传统的3D可变形模型(3DMM)多集中于面部重建,头皮区域因数据稀缺和技术限制常被忽略。2022年发表于VRCAI’22的论文《3DCMM:3DComprehensiveMorphabl 浅谈Spring的事件驱动机制 anm10387 java 浅谈Spring的事件驱动机制前言:每次去翻源码都是出于项目需要,越发觉得自己的主动性比较弱,但偶尔被逼着把源码翻一遍,也着实能收获不少。这次翻看Spring对事件处理机制的代码,主要是因为现在的项目将会话Session放在了ThreadLocal里,而异步的事件处理对线程是不共享的,为了确认这一点,将整个过程DEBUG了多遍,记录一些收获。Spring对事件的支持ApplicationEvent USearch: 高效紧凑的单文件向量搜索引擎 eahba 搜索引擎python 技术背景介绍近年来,向量搜索技术被广泛应用于诸如推荐系统、图像检索和自然语言处理等领域。FAISS是一个广泛使用的向量搜索库,但我们今天要介绍的是USearch,它是一个更小、更快的单文件向量搜索引擎。虽然USearch和FAISS都采用了HNSW(HierarchicalNavigableSmallWorld)算法,其设计原则和用户体验却有所不同。核心原理解析HNSW算法利用了小世界网络的特性, 数据挖掘十大经典算法详解(附原理解析与代码示例) IT程序媛-桃子 华为认证数据挖掘算法经验分享华为 1.PageRank(链接分析)应用场景:搜索引擎排名、社交网络分析核心原理PageRank通过网页之间的链接关系计算网页的重要性,影响力大的网页排名更高。网页影响力=所有入链页面的加权影响力之和阻尼因子D(通常设为0.85)用于模拟用户随机访问网页的行为代码示例importnetworkxasnxG=nx.DiGraph()G.add_edges_from([("A","B"),("A","C" 算法 单链的创建与删除 换个号韩国红果果 c算法 先创建结构体 struct student { int data; //int tag;//标记这是第几个 struct student *next; }; // addone 用于将一个数插入已从小到大排好序的链中 struct student *addone(struct student *h,int x){ if(h==NULL) //?????? 《大型网站系统与Java中间件实践》第2章读后感 白糖_ java中间件 断断续续花了两天时间试读了《大型网站系统与Java中间件实践》的第2章,这章总述了从一个小型单机构建的网站发展到大型网站的演化过程---整个过程会遇到很多困难,但每一个屏障都会有解决方案,最终就是依靠这些个解决方案汇聚到一起组成了一个健壮稳定高效的大型系统。 看完整章内容, zeus持久层spring事务单元测试 deng520159 javaDAOspringjdbc 今天把zeus事务单元测试放出来,让大家指出他的毛病, 1.ZeusTransactionTest.java 单元测试 package com.dengliang.zeus.webdemo.test; import java.util.ArrayList; import java.util.List; import org.junit.Test; import Rss 订阅 开发 周凡杨 htmlxml订阅rss规范 RSS是 Really Simple Syndication的缩写(对rss2.0而言,是这三个词的缩写,对rss1.0而言则是RDF Site Summary的缩写,1.0与2.0走的是两个体系)。 RSS 分页查询实现 g21121 分页查询 在查询列表时我们常常会用到分页,分页的好处就是减少数据交换,每次查询一定数量减少数据库压力等等。 按实现形式分前台分页和服务器分页: 前台分页就是一次查询出所有记录,在页面中用js进行虚拟分页,这种形式在数据量较小时优势比较明显,一次加载就不必再访问服务器了,但当数据量较大时会对页面造成压力,传输速度也会大幅下降。 服务器分页就是每次请求相同数量记录,按一定规则排序,每次取一定序号直接的数据 spring jms异步消息处理 510888780 jms spring JMS对于异步消息处理基本上只需配置下就能进行高效的处理。其核心就是消息侦听器容器,常用的类就是DefaultMessageListenerContainer。该容器可配置侦听器的并发数量,以及配合MessageListenerAdapter使用消息驱动POJO进行消息处理。且消息驱动POJO是放入TaskExecutor中进行处理,进一步提高性能,减少侦听器的阻塞。具体配置如下: highCharts柱状图 布衣凌宇 hightCharts柱图 第一步:导入 exporting.js,grid.js,highcharts.js;第二步:写controller @Controller@RequestMapping(value="${adminPath}/statistick")public class StatistickController { private UserServi 我的spring学习笔记2-IoC(反向控制 依赖注入) aijuans springmvcSpring 教程spring3 教程Spring 入门 IoC(反向控制 依赖注入)这是Spring提出来了,这也是Spring一大特色。这里我不用多说,我们看Spring教程就可以了解。当然我们不用Spring也可以用IoC,下面我将介绍不用Spring的IoC。 IoC不是框架,她是java的技术,如今大多数轻量级的容器都会用到IoC技术。这里我就用一个例子来说明: 如:程序中有 Mysql.calss 、Oracle.class 、SqlSe TLS java简单实现 antlove javasslkeystoretlssecure 1. SSLServer.java package ssl; import java.io.FileInputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.KeyStore; import Zip解压压缩文件 百合不是茶 Zip格式解压Zip流的使用文件解压 ZIP文件的解压缩实质上就是从输入流中读取数据。Java.util.zip包提供了类ZipInputStream来读取ZIP文件,下面的代码段创建了一个输入流来读取ZIP格式的文件; ZipInputStream in = new ZipInputStream(new FileInputStream(zipFileName)); &n underscore.js 学习(一) bijian1013 JavaScriptunderscore 工作中需要用到underscore.js,发现这是一个包括了很多基本功能函数的js库,里面有很多实用的函数。而且它没有扩展 javascript的原生对象。主要涉及对Collection、Object、Array、Function的操作。 学 java jvm常用命令工具——jstatd命令(Java Statistics Monitoring Daemon) bijian1013 javajvmjstatd 1.介绍 jstatd是一个基于RMI(Remove Method Invocation)的服务程序,它用于监控基于HotSpot的JVM中资源的创建及销毁,并且提供了一个远程接口允许远程的监控工具连接到本地的JVM执行命令。 jstatd是基于RMI的,所以在运行jstatd的服务 【Spring框架三】Spring常用注解之Transactional bit1129 transactional Spring可以通过注解@Transactional来为业务逻辑层的方法(调用DAO完成持久化动作)添加事务能力,如下是@Transactional注解的定义: /* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 我(程序员)的前进方向 bitray 程序员 作为一个普通的程序员,我一直游走在java语言中,java也确实让我有了很多的体会.不过随着学习的深入,java语言的新技术产生的越来越多,从最初期的javase,我逐渐开始转变到ssh,ssi,这种主流的码农,.过了几天为了解决新问题,webservice的大旗也被我祭出来了,又过了些日子jms架构的activemq也开始必须学习了.再后来开始了一系列技术学习,osgi,restful..... nginx lua开发经验总结 ronin47 使用nginx lua已经两三个月了,项目接开发完毕了,这几天准备上线并且跟高德地图对接。回顾下来lua在项目中占得必中还是比较大的,跟PHP的占比差不多持平了,因此在开发中遇到一些问题备忘一下 1:content_by_lua中代码容量有限制,一般不要写太多代码,正常编写代码一般在100行左右(具体容量没有细心测哈哈,在4kb左右),如果超出了则重启nginx的时候会报 too long pa java-66-用递归颠倒一个栈。例如输入栈{1,2,3,4,5},1在栈顶。颠倒之后的栈为{5,4,3,2,1},5处在栈顶 bylijinnan java import java.util.Stack; public class ReverseStackRecursive { /** * Q 66.颠倒栈。 * 题目:用递归颠倒一个栈。例如输入栈{1,2,3,4,5},1在栈顶。 * 颠倒之后的栈为{5,4,3,2,1},5处在栈顶。 *1. Pop the top element *2. Revers 正确理解Linux内存占用过高的问题 cfyme linux Linux开机后,使用top命令查看,4G物理内存发现已使用的多大3.2G,占用率高达80%以上: Mem: 3889836k total, 3341868k used, 547968k free, 286044k buffers Swap: 6127608k total,&nb [JWFD开源工作流]当前流程引擎设计的一个急需解决的问题 comsci 工作流 当我们的流程引擎进入IRC阶段的时候,当循环反馈模型出现之后,每次循环都会导致一大堆节点内存数据残留在系统内存中,循环的次数越多,这些残留数据将导致系统内存溢出,并使得引擎崩溃。。。。。。 而解决办法就是利用汇编语言或者其它系统编程语言,在引擎运行时,把这些残留数据清除掉。 自定义类的equals函数 dai_lm equals 仅作笔记使用 public class VectorQueue { private final Vector<VectorItem> queue; private class VectorItem { private final Object item; private final int quantity; public VectorI Linux下安装R语言 datageek R语言 linux 命令如下:sudo gedit /etc/apt/sources.list1、deb http://mirrors.ustc.edu.cn/CRAN/bin/linux/ubuntu/ precise/ 2、deb http://dk.archive.ubuntu.com/ubuntu hardy universesudo apt-key adv --keyserver ke 如何修改mysql 并发数(连接数)最大值 dcj3sjt126com mysql MySQL的连接数最大值跟MySQL没关系,主要看系统和业务逻辑了 方法一:进入MYSQL安装目录 打开MYSQL配置文件 my.ini 或 my.cnf查找 max_connections=100 修改为 max_connections=1000 服务里重起MYSQL即可 方法二:MySQL的最大连接数默认是100客户端登录:mysql -uusername -ppass 单一功能原则 dcj3sjt126com 面向对象的程序设计软件设计编程原则 单一功能原则[ 编辑] SOLID 原则 单一功能原则 开闭原则 Liskov代换原则 接口隔离原则 依赖反转原则 查 论 编 在面向对象编程领域中,单一功能原则(Single responsibility principle)规定每个类都应该有 POJO、VO和JavaBean区别和联系 fanmingxing VOPOJOjavabean POJO和JavaBean是我们常见的两个关键字,一般容易混淆,POJO全称是Plain Ordinary Java Object / Plain Old Java Object,中文可以翻译成:普通Java类,具有一部分getter/setter方法的那种类就可以称作POJO,但是JavaBean则比POJO复杂很多,JavaBean是一种组件技术,就好像你做了一个扳子,而这个扳子会在很多地方被 SpringSecurity3.X--LDAP:AD配置 hanqunfeng SpringSecurity 前面介绍过基于本地数据库验证的方式,参考http://hanqunfeng.iteye.com/blog/1155226,这里说一下如何修改为使用AD进行身份验证【只对用户名和密码进行验证,权限依旧存储在本地数据库中】。 将配置文件中的如下部分删除: <!-- 认证管理器,使用自定义的UserDetailsService,并对密码采用md5加密--> mac mysql 修改密码 IXHONG mysql $ sudo /usr/local/mysql/bin/mysqld_safe –user=root & //启动MySQL(也可以通过偏好设置面板来启动)$ sudo /usr/local/mysql/bin/mysqladmin -uroot password yourpassword //设置MySQL密码(注意,这是第一次MySQL密码为空的时候的设置命令,如果是修改密码,还需在- 设计模式--抽象工厂模式 kerryg 设计模式 抽象工厂模式: 工厂模式有一个问题就是,类的创建依赖于工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。我们采用抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。 总结:这个模式的好处就是,如果想增加一个功能,就需要做一个实现类, 评"高中女生军训期跳楼” nannan408 首先,先抛出我的观点,各位看官少点砖头。那就是,中国的差异化教育必须做起来。 孔圣人有云:有教无类。不同类型的人,都应该有对应的教育方法。目前中国的一体化教育,不知道已经扼杀了多少创造性人才。我们出不了爱迪生,出不了爱因斯坦,很大原因,是我们的培养思路错了,我们是第一要“顺从”。如果不顺从,我们的学校,就会用各种方法,罚站,罚写作业,各种罚。军 scala如何读取和写入文件内容? qindongliang1922 javajvmscala 直接看如下代码: package file import java.io.RandomAccessFile import java.nio.charset.Charset import scala.io.Source import scala.reflect.io.{File, Path} /** * Created by qindongliang on 2015/ C语言算法之百元买百鸡 qiufeihu c算法 中国古代数学家张丘建在他的《算经》中提出了一个著名的“百钱买百鸡问题”,鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁,母,雏各几何? 代码如下: #include <stdio.h> int main() { int cock,hen,chick; /*定义变量为基本整型*/ for(coc Hadoop集群安全性:Hadoop中Namenode单点故障的解决方案及详细介绍AvatarNode wyz2009107220 NameNode 正如大家所知,NameNode在Hadoop系统中存在单点故障问题,这个对于标榜高可用性的Hadoop来说一直是个软肋。本文讨论一下为了解决这个问题而存在的几个solution。 1. Secondary NameNode 原理:Secondary NN会定期的从NN中读取editlog,与自己存储的Image进行合并形成新的metadata image 优点:Hadoop较早的版本都自带, 按字母分类: ABCDEFGHIJKLMNOPQRSTUVWXYZ其他