并发编程原理与实战(三十四)ThreadLocal核心API与线程安全数据隔离机制深度解析

C# AsyncLocal原理与实战:异步上下文传递与线程安全实践 在多线程与异步编程中,线程间数据传递是常见挑战。传统静态变量存在线程安全问题,而参数层层传递在异步场景下变得笨拙。其核心原理基于执行上下文流动机制,确保数据在异步控制流中保持逻辑隔离。这一技术为Web请求上下文管理、分布式追踪等场景提供了优雅解决方案。通过AsyncLocal,开发者可以安全传递TraceId、用户身份等横切关注点,避免上下文污染。本文结合实战案例,深入解析其与ThreadLocal的本质区别,并探讨值类型陷阱、ConfigureAwait影响等高级主题,帮助构建健壮的异步应用架构。 阅读详情

前一篇我们学习了基于AQS框架下简易的自定锁的实现过程,接下来我们来学习并发编程中常见的一些类,本文先来学习ThreadLocal。

背景与基础概念

为什么需要ThreadLocal?我们已经知道加锁或者使用原子类可以保证线程安全的访问共享资源,这两种方式本质上都是多线程竞争共享资源,但是某些场景下我们只需要多线程并发安全执行,不需要访问共享资源,也就是线程之间相互隔离,线程只访问自己独享的资源,在此背景下引入了ThreadLocal。

ThreadLocal是什么?ThreadLocal是Java中用于实现线程数据隔离的类,位于java.lang包下。通过为每个线程创建独立的局部变量,实现线程间的数据隔离。这种设计避免了多线程共享变量时的同步竞争,无需依赖锁机制即可保证线程安全。

相比synchronized等技术采用"时间换空间"的策略(通过加锁排队访问共享资源),ThreadLocal采用"空间换时间"策略(为每个线程分配线程私有变量),消除了锁竞争带来的性能损耗,尤其适合高频访问的线程局部变量场景。

认识ThreadLocal类

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
 *
 * <p>For example, the class below generates unique identifiers local to each
 * thread.
 * A thread's id is assigned the first time it invokes {@code ThreadId.get()}
 * and remains unchanged on subsequent calls.
 * <pre>
 * import java.util.concurrent.atomic.AtomicInteger;
 *
 * public class ThreadId {
 *     // Atomic integer containing the next thread ID to be assigned
 *     private static final AtomicInteger nextId = new AtomicInteger(0);
 *
 *     // Thread local variable containing each thread's ID
 *     private static final ThreadLocal&lt;Integer&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#64;Override protected Integer initialValue() {
 *                 return nextId.getAndIncrement();
 *         }
 *     };
 *
 *     // Returns the current thread's unique ID, assigning it if necessary
 *     public static int get() {
 *         return threadId.get();
 *     }
 * }
 * </pre>
 * <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).
 * @param <T> the type of the thread local's value
 *
 * @author  Josh Bloch and Doug Lea
 * @since   1.2
 */
public class ThreadLocal<T> {
...
}

此类提供线程局部变量。这些变量与普通变量的区别在于:访问它们的每个线程(通过其get或set方法)都会获得该变量的独立初始化的副本。ThreadLocal实例通常作为希望将状态与线程关联的类的私有静态字段(例如用户ID或事务ID)。

例如,下面的类为每个线程生成唯一的局部标识符。线程的ID在首次调用ThreadId.get()时分配,后续调用保持不变。

import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
  // 包含待分配的下一个线程ID的原子整数
  private static final AtomicInteger nextId = new AtomicInteger(0);

  // 包含每个线程ID的线程局部变量
  private static final ThreadLocal<Integer> threadId =
      new ThreadLocal<Integer>() {
          @Override protected Integer initialValue() {
              return nextId.getAndIncrement();
      }
  };

  //返回当前线程的唯一ID,必要时分配
  public static int get() {
      return threadId.get();
  }
}

只要线程存活且ThreadLocal实例可访问,每个线程都会隐式持有其线程局部变量的副本;当线程结束时,所有线程局部变量的副本都将被垃圾回收(除非存在对这些副本的其他引用)。

我们创建5个线程对上面这个例子进行测试:

/**
 * 测试用例
 */
public static void main(String[] args) {
    // 创建5个测试线程
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            // 首次获取ID
            System.out.println(Thread.currentThread().getName() +
                    " ID: " + ThreadId.get());
            // 再次验证ID不变
            System.out.println(Thread.currentThread().getName() +
                    " Confirm ID: " + ThreadId.get());
        }).start();
    }
}

运行结果:

Thread-0 ID: 0
Thread-0 Confirm ID: 0
Thread-4 ID: 4
Thread-4 Confirm ID: 4
Thread-1 ID: 1
Thread-1 Confirm ID: 1
Thread-3 ID: 2
Thread-3 Confirm ID: 2
Thread-2 ID: 3
Thread-2 Confirm ID: 3

从运行结果可以看出,每个线程首次获取ID后,这个ID就只跟该线程有关,该线程后续获取ID时保持不变,而其他线程是获取不到该线程的的ID的,这样就做到了线程之间的数据隔离,那么这样的机制是如何实现的?下面我们就就通过分析ThreadLocal的核心API来解答这个问题。

ThreadLocal的核心API

ThreadLocal的核心API提供了线程局部变量的完整生命周期管理。下图是基于jdk21的ThreadLocal类结构图。
在这里插入图片描述

构造函数

匿名内部类重写 initialValue() 创建实例

/**
 * Creates a thread local variable.
 * @see #withInitial(java.util.function.Supplier)
 */
public ThreadLocal() {
}

看到这里可能会有一个疑问,文章开头的例子中通过new ThreadLocal() {}的方式创建实例,也就是通过匿名内部类重写 initialValue() 的方式创建ThreadLocal实例。而ThreadLocal构造函数却是空的?

ThreadLocal 是一个泛型类,但它的构造函数是空的,这是因为 Java 的泛型信息在编译时会被擦除,运行时无法获取具体的类型参数。泛型擦除意味着在编译期间,所有的泛型信息都会被移除,而在运行时,所有泛型类型都转换为其边界类型(通常是 Object)。

虽然构造函数本身没有泛型参数,但 ThreadLocal 的类型安全性通过编译器类型检查、get/set 方法的泛型参数类型检查等方式保证。

使用withInitial() 方法创建实例

通过匿名内部类重写 initialValue() 的方式创建ThreadLocal实例是Java 8之前的写法,Java 8 及以后‌推荐使用 withInitial() 方法

/**
 * Creates a thread local variable. The initial value of the variable is
 * determined by invoking the {@code get} method on the {@code Supplier}.
 *
 * @param <S> the type of the thread local's value
 * @param supplier the supplier to be used to determine the initial value
 * @return a new thread local variable
 * @throws NullPointerException if the specified supplier is null
 * @since 1.8
 */
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    return new SuppliedThreadLocal<>(supplier);
}

该方法是一个静态方法,用于创建一个线程局部变量。该变量的初始值通过 Supplier 函数式接口来提供初始值。
@param 线程局部变量值的类型
@param supplier 用于确定初始值的供应商
@return 一个新的线程局部变量
@throws NullPointerException 如果指定的供应商为 null

这样代码更简洁了,如下:

// 创建ThreadLocal实例
private static final ThreadLocal<Integer> threadId =
        ThreadLocal.withInitial(() -> nextId.getAndIncrement());

() -> nextId.getAndIncrement() 是 Java 中的 Lambda 表达式写法,它定义了一个不接受任何参数并返回 nextId.getAndIncrement() 结果的无参函数式接口实现。

set/get相关方法

set(T value)方法

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    set(Thread.currentThread(), value);
    if (TRACE_VTHREAD_LOCALS) {
        dumpStackIfVirtualThread();
    }
}

将当前线程中此线程局部变量设置为指定值。大多数子类无需重写此方法,仅依赖initialValue 方法来设置线程局部变量的值。@param value 要存储在当前线程的此线程局部变量中的值。内部调用set(Thread t, T value)方法:

private void set(Thread t, T value) {
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}

set(Thread t, T value)方法以当前线程对象t和要存储到线程局部变量的值value为参数。

/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

根据当前线程对象t获取ThreadLocal.ThreadLocalMap类型的变量threadLocals。

public class Thread implements Runnable {
...
/*
 * ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class.
 */
ThreadLocal.ThreadLocalMap threadLocals;
...
}

变量threadLocals是线程类Thread 中的属性,用于存储线程的私有数据,这个印证了文章开头说的每个线程有独立的局部变量。

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

如果变量threadLocals为空则调用createMap(Thread t, T firstValue)方法创建ThreadLocalMap对象。

ThreadLocalMap

/**
 * ThreadLocalMap is a customized hash map suitable only for
 * maintaining thread local values. No operations are exported
 * outside of the ThreadLocal class. The class is package private to
 * allow declaration of fields in class Thread.  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.
 */
static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    /**
     * The initial capacity -- MUST be a power of two.
     */
    private static final int INITIAL_CAPACITY = 16;

    /**
     * The table, resized as necessary.
     * table.length MUST always be a power of two.
     */
    private Entry[] table;

    /**
     * The number of entries in the table.
     */
    private int size = 0;

    /**
     * The next size value at which to resize.
     */
    private int threshold; // Default to 0    
    ...
}

ThreadLocalMap 是一种专为维护线程局部变量而设计的定制化哈希表。其所有操作均仅在ThreadLocal 类内部使用。该类被声明为包级私有(default权限,也称为包访问权限或默认权限),以便在 Thread 类中声明字段(Thread 类与ThreadLocal类均在java.lang包下)。为了处理大规模且长期存在的使用场景,哈希表条目使用 WeakReference(弱引用)作为键。但由于未使用引用队列,只有当哈希表空间不足时,才会确保清除过期的条目。

该哈希表中的条目继承自WeakReference,使用其主引用字段作为键(该键始终是一个ThreadLocal对象)。请注意,空键(即entry.get() == null)表示该键不再被引用,因此可以将该条目从表中清除。在后续代码中,此类条目被称为“陈旧条目”。

总的来讲,化哈希表采用Entry数组存储数据,每个Entry使用弱引用指向ThreadLocal对象作为key,实际存储的变量值作为value。

get()方法

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    return get(Thread.currentThread());
}

返回当前线程中此线程局部变量的值,内部调用get(Thread t)方法。

private T get(Thread t) {
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T) e.value;
            return result;
        }
    }
    return setInitialValue(t);
}

获取当前线程对象的局部变量哈希表ThreadLocalMap,如果不为空则通过ThreadLocal对象(键)获取线程的局部变量值。如果线程的局部变量哈希表ThreadLocalMap为空,则调用setInitialValue(Thread t)方法返回初始化的值。

/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue(Thread t) {
    T value = initialValue();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
    if (this instanceof TerminatingThreadLocal<?> ttl) {
        TerminatingThreadLocal.register(ttl);
    }
    if (TRACE_VTHREAD_LOCALS) {
        dumpStackIfVirtualThread();
    }
    return value;
}

initialValue方法就是我们创建ThreadLocal对象时重写的方法。

initialValue()

/**
 * Returns the current thread's "initial value" for this
 * thread-local variable.  This method will be invoked the first
 * time a thread accesses the variable with the {@link #get}
 * method, unless the thread previously invoked the {@link #set}
 * method, in which case the {@code initialValue} method will not
 * be invoked for the thread.  Normally, this method is invoked at
 * most once per thread, but it may be invoked again in case of
 * subsequent invocations of {@link #remove} followed by {@link #get}.
 *
 * @implSpec
 * This implementation simply returns {@code null}; if the
 * programmer desires thread-local variables to have an initial
 * value other than {@code null}, then either {@code ThreadLocal}
 * can be subclassed and this method overridden or the method
 * {@link ThreadLocal#withInitial(Supplier)} can be used to
 * construct a {@code ThreadLocal}.
 *
 * @return the initial value for this thread-local
 * @see #withInitial(java.util.function.Supplier)
 */
protected T initialValue() {
    return null;
}

返回当前线程中此线程局部变量的“初始值”。当线程首次通过 get方法访问该变量时,此方法将被调用,除非该线程之前已调用过set 方法,在这种情况下,该线程不会调用initialValue方法(因为哈希表已创建)。通常情况下,此方法在每个线程中最多调用一次,但如果在调用remove后再次调用get,则可能会再次调用此方法。

此实现直接返回null;如果程序员希望线程局部变量的初始值不为null,则可以子类化ThreadLocal并重写此方法,或者使用ThreadLocal#withInitial(java.util.function.Supplier)方法来构造一个ThreadLocal。

remove()方法

/**
* Removes the current thread's value for this thread-local
* variable.  If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim.  This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
 remove(Thread.currentThread());
}

private void remove(Thread t) {
     ThreadLocalMap m = getMap(t);
     if (m != null) {
         m.remove(this);
     }
 }

移除当前线程中此线程局部变量的值。如果当前线程随后通过get读取该线程局部变量,其值将通过调用initialValue方法重新初始化,除非在此期间当前线程通过set设置了该值。这可能导致当前线程多次调用initialValue 方法。

get/set方法使用示例

对文章开头的例子进行改造下,在首次获取线程ID前设置线程的局部变量。

/**
 * 测试用例
 */
public static void main(String[] args) {
    // 创建5个测试线程
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            // 设置值
            threadId.set(threadId.get() + 10);
            // 首次获取ID
            System.out.println(Thread.currentThread().getName() +
                    " ID: " + ThreadId.get());
            // 再次验证ID不变
            System.out.println(Thread.currentThread().getName() +
                    " Confirm ID: " + ThreadId.get());
        }).start();
    }
}

运行结果:

Thread-1 ID: 11
Thread-1 Confirm ID: 11
Thread-4 ID: 14
Thread-4 Confirm ID: 14
Thread-2 ID: 12
Thread-2 Confirm ID: 12
Thread-0 ID: 10
Thread-0 Confirm ID: 10
Thread-3 ID: 13
Thread-3 Confirm ID: 13

总结

通过分析ThreadLocal的核心API,我们总结出ThreadLocal线程安全的数据隔离机制:

1、ThreadLocal通过为每个线程创建独立的局部变量来实现线程安全的数据隔离,其核心机制基于Thread、ThreadLocal和ThreadLocalMap三者的协作。

2、每个Thread对象内部维护一个ThreadLocalMap实例作为线程的私有存储空间。当调用ThreadLocal的set()方法时,系统会获取当前线程的ThreadLocalMap,并以ThreadLocal对象作为键、要存储的数据作为值进行存储。同样,get()方法通过当前线程和ThreadLocal对象来获取对应的值。

3、这种设计实现了真正的线程数据隔离,每个线程操作的都是自己的私有变量,从根本上避免了多线程访问共享资源带来的竞争问题。与传统的同步机制(如synchronized)通过加锁控制共享资源访问不同,ThreadLocal采用"空间换安全"策略,无需显式加锁即可保证线程安全。

直流有刷电机控制 - 驱动电路(二) 1.如图所示为最常见的推挽驱动电路,我附加了具有正反转功能的电机驱动控制,正反转是由两个继电器组成。使用推挽电路来驱动NMOS管时充电电流和放电电流都很大,因此NMOS管导通快,关断也快,而且NMOS温度不会很高。下面来介绍一下该电路的原理。(1)PWM_MCU是由单片机给出的PWM波信号,该信号是由一些列高低电平的方波构成。当信号为高电平的时候Q3导通,经过R47再到Q5的g极,此时mos导通,如果是K1闭合是正转那么K2闭合就是反转。(2)当PWM低电平时使得Q3关断,Q6导通。 阅读详情

相关推荐

电子工程师日记:用STM32F4的DCMI接口驱动OV2640时,那些数据同步的坑我都踩过了

本文分享了使用STM32F4的DCMI接口驱动OV2640摄像头时,在数据同步方面遇到的典型问题与解决方案。作者通过示波器分析PCLK相位、纠正DCMI_CR寄存器中同步信号极性的配置误区,并排查DMA传输瓶颈与FIFO溢出,最终实现了稳定的图像采集。文章为嵌入式工程师提供了宝贵的硬件调试经验与配置清单。

weixin_29189377的博客 156

ThreadLocal 深度解析:原理、实战与避坑指南

ThreadLocal是Java实现线程数据隔离的核心机制,通过为每个线程创建变量副本避免共享资源的竞争问题。重点指出内存泄漏风险源于线程长期存活时value的强引用,强调使用后必须remove()清理。最佳实践包括声明为static final、避免存储大对象、线程池场景自动清理等。ThreadLocal无需加锁即可实现,但需谨慎使用以避免潜在问题。

J_liaty的博客 1069

uniCloud云存储进阶:七牛云扩展存储从开通、绑定域名到批量上传的完整实战

本文详细介绍了uniCloud云存储进阶实践,重点讲解如何通过七牛云扩展存储实现从开通、绑定域名到批量上传的完整流程。内容涵盖成本优势分析、域名备案方案、SSL证书配置、多文件并发上传控制等实战技巧,并提供可直接复用的代码示例,帮助开发者高效构建内容社区类应用。

weixin_26985923的博客 468

ThreadLocal:线程私有世界的守门人 —— 从原理到 10 大实战场景深度解析

ThreadLocal是Java并发编程中的重要工具,通过为每个线程创建变量的私有副本实现线程数据隔离。本文深入解析了ThreadLocal的核心原理与10大应用场景,包括解决SimpleDateFormat线程安全问题、数据库连接管理、Web用户上下文、日志追踪等。

果酱 の 博客 1007

汽车LED驱动芯片TPS92682-Q1 SPI接口配置与调试实战指南

SPI(串行外设接口)作为一种高速、全双工的同步串行通信总线,广泛应用于微控制器与外围设备之间的数据交换。其工作原理基于主从架构,通过时钟、数据线和片选信号实现设备间的精准时序控制。在汽车电子和工业控制领域,SPI因其简单高效、易于扩展的特性,成为配置复杂电源管理芯片、传感器和存储器的关键技术。通过SPI接口,工程师能够动态调整设备参数、实时读取状态信息,并实现复杂的故障诊断与保护策略,极大地提升了系统的灵活性与可靠性。本文以汽车级双通道LED恒流驱动器TPS92682-Q1为例,深入解析如何通过SPI接口

weixin_29185493的博客 288

RC电路(一):微分|积分

的取值不同,会导致输出波形和输入波形之间的关系也不同,由此也会产生不同的应用。输出为0,随着电容充满电,运放-输入端得到的分压为正最大值,可等效成一个电压源,且电压不可突变,此时电流反向为最大值,开始充电时的容抗为0,电压不可突变则电压差为0,运放。开始充电时的容抗为0,电压不可突变则电压差为0,运放。输入端得到的分压也不可突变,随着电容放完电,于是。,因电容上电压不能突变(来不及充电,相当于短路,由上分析可得积分运算电路中,输出为运放的负最大峰值,随着电容充满电,输出为运放的正最大峰值,随着电容放完电,

既有随处可栖的江湖,也有追风逐梦的骁勇! 1万+

ThreadLocal 深度解析:它解决了什么、原理是什么、如何正确使用(含代码与实战建议)

本文从问题出发,全面解析 Java 的 ThreadLocal:它为每个线程维护独立变量副本,解决线程间共享冲突与跨层传递上下文的需求。深入讲解 ThreadLocal 的内部实现(ThreadLocalMap、弱引用 key)、InheritableThreadLocal 的复制行为、以及常见导致内存泄漏的场景与根本原因。基于实现细节,文章给出清晰且可执行的最佳实践(withInitial、try-finally 中 remove()、线程池上下文传递策略等)并通过示例代码说明如何安全高效地使用 Thre

往者之不谏 来者犹可追 1898

交换机背板带宽、交换容量与包转发率到底是啥?

在网络工程中,交换机是核心设备之一,而关于它的性能指标,背板带宽、交换容量、以及包转发率往往成为人们衡量其性能的重要参数。这些术语的定义、关系、以及在实际场景中的应用,都对交换机的选型与部署有着重要意义。本文将为您全面解读这些概念,帮助您深入了解交换机的核心性能指标。

网络技术联盟站 1548

Java ThreadLocal 深度解析与最佳实践指南

Java ThreadLocal 深度解析与最佳实践指南

嗨,欢迎来到我的 优快云 博客小天地!一名深耕多年的技术发烧友。在这里,我将把日常工作中积累的宝贵经验,从复杂架构设计的精妙之处,到代码优化的实战技巧,毫无保留地分享给大家。 4924

YOLOv11与CLIP融合的零样本目标检测技术:原理、实现与创新实践

YOLO(You Only Look Once)系列作为单阶段检测器的代表,以其实时高效的特性在工业界获得广泛应用。YOLOv11作为该系列的最新演进,引入了多项架构创新: C3K2模块:采用双小卷积核替代传统大卷积核,在保持感受野的同时减少计算量。当参数c3k=False时退化为YOLOv8的C2F模块,提供灵活的精度-效率权衡。 C2PSA(Convolutional block with Parallel Spatial Attention):通过并行空间注意力机制增强对微小目标的特征提取能力,这对

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1068

史上最全!研究生科研必备科研工具(含AI科研工具),附使用说明和示例

这些工具在科研中的应用非常广泛,可以帮助研究生更高效地完成研究任务。为了更好地利用这些工具,建议深入学习它们的官方文档和教程,或者参加相关的工作坊和培训。

智能AI让学术科研更容易 3941

Java ThreadLocal 深度解析:原理、场景与最佳实践

ThreadLocal是Java提供的线程本地变量机制,通过为每个线程维护独立变量副本来实现线程数据隔离。核心特性包括线程隔离、无锁并发和自动回收。主要应用场景包括数据库连接管理、会话信息传递、事务管理、线程安全缓存等。使用时需注意:1)通过set/get/remove方法操作变量;2)使用后必须remove防止内存泄漏;3)线程池场景需特别处理。高级用法包括泛型支持、延迟初始化、InheritableThreadLocal等。最佳实践是在finally块中清理变量,避免线程复用导致数据错乱。Spring等

huahua8088的博客 1580

浏览器缓存导致代码不更新?一招解决部署后用户仍看到旧页面的问题

方案效果适用场景文件名哈希永久解决前端工程化项目HTTP缓存头控制精准控制需服务器权限Meta标签部分生效临时补救方案最佳实践:入口HTML禁用缓存,静态资源使用哈希文件名 + 长期缓存,达到「秒级更新」+「高性能」的平衡!

江拥羡橙的博客 810

ThreadLocal 深度解析:原理、用法、坑点与大厂面试指南

本文全面解析Java并发编程核心工具ThreadLocal,涵盖其基础用法、底层原理、实战场景及避坑指南。ThreadLocal通过为每个线程创建独立变量副本实现线程隔离,解决多线程共享变量的安全问题,相比加锁方案性能更优。重点剖析了ThreadLocalMap数据结构及内存泄漏问题(Entry中key弱引用、value强引用),强调使用后必须remove()的规范。文章还对比了ThreadLocal与synchronized差异,介绍了InheritableThreadLocal特性,并针对线程池场景给出

m0_75223236的博客 1054

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ThreadLocal 深度解析:Java 并发编程中的 “线程私有” 神器

ThreadLocal 是 Java 并发编程中实现线程私有变量的核心工具类,凭借无锁隔离设计,为每个线程分配独立资源副本,彻底规避多线程共享资源的并发冲突,在业务开发与框架底层应用广泛。本文先明确其核心价值,介绍线程上下文存储、避免参数传递冗余等典型场景,以及在 Spring 事务、MyBatis 会话管理中的框架应用;再深入剖析 Thread、ThreadLocal、ThreadLocalMap 的底层关联,拆解核心方法原理与关键设计细节;最后针对内存泄漏、线程池数据污染等常见问题提供避坑指南。

qq_59219765的博客 737

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

深入剖析Java线程局部变量:ThreadLocal的核心原理与正确实践

特性。

2401_87398486的博客 1240

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【Java多线程编程实战核心原理与线程安全解决方案深度解析】

线程是操作系统调度的基本单位,而 Java 线程通过 `Thread` 或 `Runnable` 接口封装操作系统的原生线程。Java 的多线程机制同时支持两者。- Volatile 变量的作用:其强制保证对变量读写的可见性,禁止指令重排序,但仅能体现最终一致性,无法替代互斥锁(如 `synchronized`)解决竞态条件。- 实现:`java.util.concurrent.atomic` 包:`AtomicInteger` 的 `compareAndSet()` 方法即基于 CAS。

2501_93950968的博客 1158

ScopedValue vs ThreadLocal:Java并发编程新旧方案全面对比

本篇作为系列文章的对比篇,我们将从底层设计、功能特性、性能表现、适用场景四个维度,对 ScopedValue 和 ThreadLocal 进行全方位对比,帮你理清二者的边界,做出最适合业务的选择。

y245166349的专栏 805

大厂Java面试深度解析:核心、并发与Spring Boot实战

Java作为企业级开发的主流语言,其核心机制与并发编程是开发者必须掌握的基础。JVM内存模型揭示了对象生命周期管理原理,而synchronized与ReentrantLock等并发工具则保障了多线程环境下的数据安全。Spring Boot通过自动配置机制显著提升了开发效率,其背后的条件装配与组件扫描技术值得深入理解。在电商、金融等高并发场景中,合理运用缓存策略与连接池监控能有效预防雪崩与泄漏问题。本文通过真实面试案例,详解HashMap并发陷阱、动态数据源配置等高频考点,为3-5年经验的Java开发者提供实

chutisun0039的博客 309

手写MyBatis第107弹:@MapperScan原理与SqlSessionTemplate线程安全机制

本文介绍了基于SpringBoot+Vue的学员管理系统,包含学员、教练和管理员三个角色的功能模块。系统运行环境要求JDK1.8、MySQL5.7+,支持多种IDE和Tomcat7.x-9.x版本。重点分析了MyBatis-Spring整合的核心机制,包括@MapperScan自动注册原理、SqlSessionTemplate线程安全实现,以及完整工程搭建指南。文章详细讲解了项目架构设计、模块划分、依赖配置、事务集成等关键内容,并提供了性能优化建议和常见问题排查方法。最后附有完整配置示例和源码获取方式,适合

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 1827

Java进阶:JVM、并发编程与框架原理深度解析

Java作为企业级开发的主流语言,其核心价值在于JVM的跨平台特性和强大的并发处理能力。理解JVM内存模型与GC原理是性能优化的基础,而多线程编程则需要掌握锁机制与并发容器的选型策略。Spring等框架通过IoC容器和AOP等设计模式实现了组件解耦,深入其源码能提升定制开发能力。本文结合电商系统内存泄漏、转账并发问题等典型案例,演示如何使用jvisualvm、MAT等工具进行性能诊断,以及如何通过状态模式优化复杂业务逻辑。对于中高级开发者,建立包含Checkstyle、JaCoCo在内的代码质量保障体系至关

weixin_33483304的博客 110

Java-JUC笔记-8 ThreadLocal核心要点

1. 定义ThreadLocal 是 Java 提供的线程级变量隔离机制,通过为每个线程创建变量的独立副本,实现线程封闭(Thread Confinement),解决多线程共享资源时的竞争问题。2. 核心价值无锁化线程安全:避免使用或Lock,以空间换时间提升并发性能。隐式传参:跨方法、跨类传递线程上下文信息(如用户身份、事务ID),避免接口污染。3. 经典应用场景Spring 事务管理(用户会话存储(数据库连接池(线程绑定 Connection)4. 核心API。

m0_56020664的博客 964

Java ThreadLocal原理与应用实战指南

ThreadLocal是Java并发编程中实现线程隔离的核心机制,通过为每个线程创建独立的变量副本来避免多线程竞争。其底层采用ThreadLocalMap数据结构,结合弱引用解决内存管理问题。在Web开发中,ThreadLocal常用于用户会话管理、数据库连接隔离等场景,能有效提升系统性能。典型应用包括Spring框架的RequestContextHolder、分布式追踪的TraceId传递等。需要注意的是,在线程池环境下必须配合remove()方法防止内存泄漏,对于JDK20+项目可考虑使用ScopedV

weixin_32642109的博客 222

【Java每日一练-Day31】ThreadLocal深度解析(全知识点文档)

/ 推荐:静态ThreadLocal。

Xiong_Ji537的博客 634

并发编程核心:线程安全与性能优化实战

并发编程是现代软件开发的核心技术,通过多线程执行提升系统吞吐量。其核心挑战在于线程安全与资源共享,需要理解竞态条件、临界区等基础概念。同步机制如synchronized、ReentrantLock等可解决线程安全问题,而线程池技术能有效管理系统资源。在高并发场景下,合理使用Atomic类、ConcurrentHashMap等并发容器可显著提升性能。典型应用包括电商秒杀、实时数据处理等需要高并发的系统。通过线程状态监控和锁优化等手段,可以解决死锁、线程泄漏等常见问题,实现从300ms到120ms的性能飞跃。

weixin_33762130的博客 376

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 221
上一篇: 开发避坑指南(63):解决SQL排序子句列名歧义异常:Column ‘xxx‘ is ambiguous
下一篇: 开发避坑指南(64):修复IllegalArgumentException:参数值类型与期望类型不匹配
帧栈
博客等级 码龄2年 335粉丝 120原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值