【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)

文章目录

  • 【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)
    • 1. 基本 API 介绍
    • 2. 简单用法
    • 3. 应用场景
    • 4. Threadlocal与Synchronized区别
    • 5. 内存溢出和内存泄漏
      • 5.2 内存溢出 (Memory Overflow)
      • 5.2 内存泄漏 (Memory Leak)
    • 6. 强引用、软引用、弱引用和虚引用
      • 6.1 强引用 (Strong Reference)
      • 6.2 软引用 (Soft Reference)
      • 6.3 弱引用 (Weak Reference)
      • 6.4 虚引用 (Phantom Reference)
      • 6.5 示例代码
    • 7. Threadlocal原理分析
    • 8. ThreadLocal 内存泄漏问题及避免方法
    • 9. ThreadLocal 的引用类型与内存泄漏问题


【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)

ThreadLocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定,Threadloca适用于在多线程的情况下,可以实现传递数据,实现线程隔离。

java.lang.ThreadLocal 是 Java 中一个用于实现线程局部变量的类。它提供了一种在每个线程中拥有独立变量副本的机制,这样每个线程都可以独立地改变自己的副本,而不会影响其他线程中的副本。

1. 基本 API 介绍

ThreadLocal 类的构造方法

  • ThreadLocal(): 创建一个新的 ThreadLocal 实例。

ThreadLocal 的主要方法

  • T get(): 返回当前线程中该 ThreadLocal 的值。如果当前线程中还没有值,则会调用 initialValue() 方法来初始化这个值。
  • void set(T value): 设置当前线程中该 ThreadLocal 的值。
  • protected T initialValue(): 返回该 ThreadLocal 的初始值。默认返回 null。子类可以重写此方法以提供不同的初始值。

ThreadLocal 的高级特性

  • void remove(): 移除当前线程中该 ThreadLocal 的值。这有助于垃圾回收。

2. 简单用法

下面是一个简单的示例来说明如何使用 ThreadLocal:

public class Example {static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0; // 设置初始值为0}};public static void main(String[] args) {// 在主线程中设置值threadLocal.set(10);System.out.println("Main thread: " + threadLocal.get());// 创建新线程并设置不同的值Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set(20);System.out.println("Thread 1: " + threadLocal.get());}});t1.start();try {t1.join(); // 等待线程结束} catch (InterruptedException e) {e.printStackTrace();}// 主线程中的值没有改变System.out.println("Main thread after thread 1: " + threadLocal.get());}
}

在这个例子中,主线程和线程 t1 都有自己的 ThreadLocal 变量副本,并且它们之间互不影响。

注意事项

  • 使用 ThreadLocal 时要小心内存泄漏的问题。如果不显式调用 remove() 方法或不通过 set(null) 清除引用,那么线程的生命周期内该 ThreadLocal 的值将一直保留,可能导致内存泄漏。
  • 如果 ThreadLocal 不再需要,建议调用 remove() 方法释放资源。

3. 应用场景

1. Spring 事务管理

在 Spring 框架中,TransactionAspectSupport 提供了对事务的支持。其中有一个方法 currentTransactionStatus() 可以返回当前线程中的事务状态,通常用来决定事务是否应该回滚。

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

**应用场景:**当某个业务逻辑执行失败时,可以通过调用 setRollbackOnly() 方法来标记当前事务需要回滚,而不是提交。这会在事务结束时被事务管理器检测到并触发回滚操作。

**为何使用 ThreadLocal:**因为事务状态是每个线程独有的,所以在不同的线程中处理不同的事务时,需要确保事务状态不被混淆。ThreadLocal 提供了这种隔离性。

2. 获取 HttpServletRequest

在 Web 应用程序中,HttpServletRequest 对象通常在每个 HTTP 请求处理过程中可用。为了在不同的层次中访问这个对象,可以使用 RequestContextHolder

HttpServletRequest request = ((ServletRequestAttributes) 											RequestContextHolder.currentRequestAttributes()).getRequest();

**应用场景:**在过滤器、拦截器或服务层等地方,可能需要访问 HTTP 请求的信息(如请求头、参数等)。

**为何使用 ThreadLocal:**每个 HTTP 请求都是在一个独立的线程中处理的。因此,RequestContextHolder 使用 ThreadLocal 来存储当前线程内的 HttpServletRequest 对象,确保每个线程只能访问与之相关的 HTTP 请求。

3. AOP 调用链传递参数

在分布式系统中,比如使用 LCN 或 Seata 这样的分布式事务框架时,需要在不同的服务间传递全局唯一的事务 ID。

**应用场景:**在发起一个分布式事务时,首先会生成一个全局唯一的事务 ID,然后通过 ThreadLocal 将这个 ID 保存在当前线程中。之后,在后续的调用中,可以从中获取这个 ID 并将其作为参数传递给下游服务。

**为何使用 ThreadLocal:**分布式事务通常涉及多个服务间的调用,每个服务调用都在单独的线程中执行。为了确保每个服务调用能够访问同一个事务 ID,使用 ThreadLocal 来存储这个 ID 是非常合适的。

总结来说,ThreadLocal 在这些场景下被广泛使用是因为它提供了每个线程的数据隔离,这对于保持线程安全和避免并发问题至关重要。特别是在涉及事务管理和跨层通信的情况下,ThreadLocal 的使用可以简化代码并减少潜在的错误。

4. 真实应用场景

例如:在aop中生成全局的id,目标方法获取aop中生成的全局id;

  • aop拦截目标方法,生成全局的id

  • doTest 如何获取在aop类中生成全局id呢

@Component
public class GlobalIDContextholder {private ThreadLocal<String> globalIds = new ThreadLocal<String>();public String getId() {return globalIds.get();}public void set(String globalId) {globalIds.set(globalId);}
}@Aspect
@Component
@Slf4j
public class TestAop {@Autowiredprivate GlobalIDContextholder globalIDContextholder;/*** 切入点*/@Pointcut("execution(public * com.mayikt.service.*Service.*(..))")public void log() {}/*** 前置操作** @param point 切入点*/@Before("log()")public void beforeLog(JoinPoint point) {UUID globalId = UUID.randomUUID();globalIDContextholder.set(globalId.toString());}
}@RestController
public class TsetController {@Autowiredprivate GlobalIDContextholder globalIDContextholder;@GetMapping("/doTest")public String doTest() {return "从aop中获取的全局id:" + globalIDContextholder.getId();}
}

4. Threadlocal与Synchronized区别

ThreadLocalsynchronized 是 Java 中用于处理多线程编程的两种不同机制。它们各自解决的问题和使用场景有所不同。

1.ThreadLocal

ThreadLocal 是一种机制,允许为每个线程维护一个独立的变量副本。这意味着每个线程都可以拥有自己的局部变量,这些变量不会与其他线程共享。

特点:

  • 线程隔离ThreadLocal 为每个线程提供了一个独立的变量副本,因此线程之间不会相互干扰。
  • 安全性:由于每个线程都有自己的变量副本,所以不存在数据竞争问题,不需要担心同步问题。
  • 使用场景ThreadLocal 适用于需要在线程之间隔离数据的情况,例如每个线程需要维护自己的数据库连接、线程局部变量等。

2.synchronized

synchronized 是 Java 中的关键字,用于实现线程之间的同步。它可以用来修饰方法或代码块,确保同一时刻只有一个线程可以执行被 synchronized 修饰的代码。

特点:

  • 互斥性synchronized 确保了同一时刻只有一个线程可以执行被修饰的方法或代码块。
  • 可见性和原子性synchronized 保证了线程之间的可见性和原子性,即一个线程对共享变量的修改对其他线程是可见的,并且操作是不可分割的。
  • 使用场景synchronized 适用于需要多个线程共享数据,并且需要确保数据一致性的情况,例如计数器、共享资源的管理等。

总结

  • ThreadLocal 用于在线程之间隔离数据,每个线程都有独立的变量副本,不需要同步。
  • synchronized 用于保证线程之间的同步,确保共享数据的一致性。

两者的主要区别在于它们解决的问题不同:ThreadLocal 解决的是线程之间的数据隔离问题,而 synchronized 解决的是线程之间的同步问题。在实际应用中,可以根据具体的需求选择合适的方法。

5. 内存溢出和内存泄漏

5.2 内存溢出 (Memory Overflow)

内存溢出是指程序在运行过程中分配的内存超过了JVM所能提供的最大内存限制,导致程序无法正常运行的一种异常情况。在Java中,内存溢出通常发生在以下几个方面:

  1. 堆内存溢出 (Heap Memory Overflow):

    • 当JVM无法为新的对象分配足够的空间时会发生这种情况。这通常是由于不断创建新对象而没有及时释放不再使用的对象,导致垃圾回收机制无法清理足够的内存空间。
    • 常见错误信息为 java.lang.OutOfMemoryError: Java heap space
  2. 栈内存溢出 (Stack Memory Overflow):

    • 发生在递归调用或者深度嵌套调用时,如果递归深度过大或函数调用栈过深,则可能导致栈溢出。
    • 常见错误信息为 java.lang.StackOverflowError
  3. 方法区溢出 (PermGen or Metaspace Overflow):

    • 发生在方法区(在JDK 8之前称为永久代 PermGen space,在JDK 8及以后版本称为元空间 Metaspace)。
    • 这通常是由于加载了过多的类或常量池过大等原因导致的。
    • 常见错误信息为 java.lang.OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: Metaspace
  4. 本地方法栈溢出 (Native Method Stack Overflow):

    • 与栈内存溢出类似,但涉及的是本地方法栈。
    • 常见错误信息为 java.lang.OutOfMemoryError: Java native method stack overflow

5.2 内存泄漏 (Memory Leak)

内存泄漏是指程序在申请内存后未能释放已分配的内存,随着时间推移,越来越多的内存被占用但不再被使用,最终可能导致程序崩溃或性能下降。内存泄漏的特点是分配的内存不会被自动回收,即使程序本身还在正常运行。

内存泄漏的表现形式包括但不限于:

  1. 未释放的对象引用:在Java中,如果一个对象不再被任何引用所指向,那么垃圾收集器会自动回收该对象。但如果存在一些无效的引用(例如静态集合中存储的对象引用),那么这些对象将不会被回收,从而导致内存泄漏。

  2. 循环引用:特别是在使用引用计数的垃圾回收机制的语言中,但在Java中,垃圾收集器会处理循环引用,因此这不是一个常见问题。

  3. 缓存管理不当:如果缓存策略不当,可能会导致缓存中存储了大量的不再需要的数据,这些数据占据的内存不会被释放。

  4. 监听器/回调注册:注册了监听器或回调但没有相应的注销操作,会导致监听器或回调对象一直被持有,从而造成内存泄漏。

  5. 日志记录:日志记录可能会保留大量的对象引用,如果没有正确的管理,也可能导致内存泄漏。

  6. 线程局部变量:使用 ThreadLocal 存储对象时,如果没有正确地清理这些对象,也可能会导致内存泄漏。

内存泄漏通常比内存溢出更难以检测和修复,因为它不一定立即导致程序崩溃,但会逐渐消耗内存资源,影响程序性能和稳定性。为了避免内存泄漏,开发人员需要仔细设计程序结构,确保所有分配的资源都能被适时地释放。

6. 强引用、软引用、弱引用和虚引用

在Java中,对象引用分为四种类型:强引用(Strong)、软引用(Soft)、弱引用(Weak)和虚引用(Phantom)。每种引用类型都有其特定的行为和用途,主要用于实现内存管理的不同需求。

6.1 强引用 (Strong Reference)

  • 定义:这是最常用的引用类型。如果一个对象有一个强引用指向它,那么垃圾收集器永远不会回收这个对象。
  • 实现:普通的对象引用就是强引用,例如 Object obj = new Object();
  • 特点:只要强引用存在,对象就不会被回收。

6.2 软引用 (Soft Reference)

  • 定义:软引用是用来描述一些非必需但仍然有用的对象。如果内存足够,软引用指向的对象不会被回收;如果内存不足,那么软引用指向的对象会被回收。
  • 实现:通过 java.lang.ref.SoftReference 类实现。
  • 特点
    • 当 JVM 认为内存不足时,它会回收软引用指向的对象。
    • 一般用于实现内存敏感的缓存。

6.3 弱引用 (Weak Reference)

  • 定义:弱引用比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。(主动调用System.gc();之前)
  • 实现:通过 java.lang.ref.WeakReference 类实现。
  • 特点
    • 在垃圾回收时一定会被回收。
    • 一般用于实现更严格的缓存策略,或者与 ReferenceQueue 结合使用。

6.4 虚引用 (Phantom Reference)

  • 定义:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它相当于没有任何引用,随时都可能被垃圾回收器回收。
  • 实现:通过 java.lang.ref.PhantomReference 类实现。
  • 特点
    • 虚引用必须与 ReferenceQueue 关联。
    • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象之前,把这个虚引用加入到与之关联的队列中。
    • 一般用于跟踪对象的销毁过程,比如实现对象的 “finalize” 通知机制。

6.5 示例代码

下面是一个简单的示例代码,演示如何使用这四种引用类型:

import java.lang.ref.*;public class ReferenceDemo {private static final int _1MB = 1024 * 1024;public static void createReferences() {// 强引用Object strongRef = new byte[4 * _1MB];// 软引用SoftReference<Object> softRef = new SoftReference<>(new byte[4 * _1MB]);// 弱引用WeakReference<Object> weakRef = new WeakReference<>(new byte[4 * _1MB]);// 虚引用PhantomReference<Object> phantomRef = new PhantomReference<>(new byte[4 * _1MB], new ReferenceQueue<>());}public static void checkReferences() {System.out.println("Checking references...");// 检查软引用if (softRef.get() != null) {System.out.println("Soft reference is still valid.");} else {System.out.println("Soft reference has been cleared.");}// 检查弱引用if (weakRef.get() != null) {System.out.println("Weak reference is still valid.");} else {System.out.println("Weak reference has been cleared.");}// 检查虚引用if (phantomRef.get() == null) {System.out.println("Phantom reference has been cleared.");} else {System.out.println("Phantom reference is still valid.");}}public static void main(String[] args) {createReferences();// 手动触发垃圾回收System.gc();try {Thread.sleep(1000); // 给垃圾收集器一点时间} catch (InterruptedException e) {e.printStackTrace();}checkReferences();}
}
  • 强引用:是最常用的引用类型,指向的对象不会被垃圾回收器回收。
  • 软引用:在内存不足时被回收,适用于实现内存敏感的缓存。
  • 弱引用:在下次垃圾回收时被回收,适用于实现更严格的缓存策略。
  • 虚引用:主要用于跟踪对象的销毁过程,需要与 ReferenceQueue 结合使用。

7. Threadlocal原理分析

  1. 在每个线程中都有自己独立的ThreadLocalMap对象,ThreadLocalMap对象底层基于Entry对象封装。
  2. 如果当前线程对应的ThreadLocalMap对象为空的情况下,则创建该ThreadLocalMap对象,并且赋值键值对。 Key 为当前new ThreadLocal()对象,value 就是为object变量值。

Set方法源码

一个线程对应一个独立ThreadLocalMap,一个ThreadLocalMap k==ThreadLocal value缓存变量的值

//ThreadLocal类
public void set(T value) {//获取到当前的线程Thread t = Thread.currentThread();//获取到当前线程中的threadLocals   threadLocals类型ThreadLocalMap 底层基于Entry对象封装//Entry对象 key=ThreadLocal value缓存的变量的值ThreadLocalMap map = getMap(t);if (map != null) {//this就是在调用这个方法之前new 出的 ThreadLocal 对象map.set(this, value);} else { createMap(t, value);}
}
//ThreadLocal类
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}// Thread 类中的
ThreadLocal.ThreadLocalMap threadLocals = null;
//ThreadLocal类中的ThreadLocalMap 类
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
}

Get方法源码

public T get() {//获取当前线程Thread t = Thread.currentThread();//获取当前线程对应的 ThreadLocalMap 对象ThreadLocalMap map = getMap(t);if (map != null) {//this就是在调用这个方法之前new 出的 ThreadLocal 对象//根据具体threadLocal对象(this)获取value值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();
}

image-20240804175926921

8. ThreadLocal 内存泄漏问题及避免方法

1.ThreadLocal 内存泄漏的原因

ThreadLocal 是 Java 中用于在线程内部存储数据的一种机制。每个线程都有一个独立的 ThreadLocalMap,用来存储线程局部变量。ThreadLocalMap 使用 Entry 对象来封装键值对,其中键是 ThreadLocal 实例,而值则是线程局部变量的实际值。

ThreadLocal 作为Entry对象的key,是弱引用,当ThreadLocal指向null的时候,Entry对象中的key变为null,该对象一直无法被垃圾收集机制回收,一直占用到了系统内存,有可能会发生内存泄漏的问题。

2.如何避免 ThreadLocal 内存泄漏

为了避免 ThreadLocal 导致的内存泄漏,可以采取以下几种措施:

  1. 手动调用 remove() 方法

    每个 ThreadLocal 实例都有一个 remove() 方法,可以用来从当前线程的 ThreadLocalMap 中移除该实例对应的条目。当不再需要某个线程局部变量时,应该显式地调用此方法来释放资源。

    myThreadLocal.remove();
    
  2. 在每次 set() 方法调用时清理 null

    ThreadLocalset() 方法会在设置新值之前检查是否有 null 键存在,并将其从 ThreadLocalMap 中移除。因此,频繁地调用 set() 方法可以帮助清理不再使用的条目。

  3. 使用弱引用

    ThreadLocal 的设计已经考虑到了内存泄漏的风险,它默认使用弱引用作为键,这样即使没有显式地调用 remove() 方法,当没有强引用指向 ThreadLocal 实例时,该实例最终也会被垃圾回收。

3.示例代码

下面是使用 ThreadLocal 并避免内存泄漏的一个示例:

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;public class SafeThreadLocal<K> extends ThreadLocal<K> {@Overrideprotected K initialValue() {return null;}@Overridepublic void remove() {super.remove();// 显式调用 remove 方法以确保从 ThreadLocalMap 中删除该条目}@Overridepublic void set(K value) {super.set(value);// 每次调用 set 时都会自动清理 null 键}// 其他方法...public static void main(String[] args) {SafeThreadLocal<String> threadLocal = new SafeThreadLocal<>();// 使用 ThreadLocalthreadLocal.set("Hello, World!");// 不再需要时,显式调用 remove 方法threadLocal.remove();}
}// 假设存在一个 ThreadLocalMap 的实现,类似于如下所示
class ThreadLocalMap {private final Map<WeakReference<ThreadLocal<?>>, Object> map = new HashMap<>();public void put(ThreadLocal<?> key, Object value) {map.put(new WeakReference<>(key), value);}public Object get(ThreadLocal<?> key) {return map.get(new WeakReference<>(key));}public void remove(ThreadLocal<?> key) {map.remove(new WeakReference<>(key));}// 其他方法...
}

通过上述措施,可以有效地避免由 ThreadLocal 引发的内存泄漏问题。

9. ThreadLocal 的引用类型与内存泄漏问题

  • **强引用作为键:**如果 ThreadLocal 的键使用强引用,当我们将 ThreadLocal 的引用设为 null 时,尽管 ThreadLocal 对象本身可能变得不可达,但由于每个线程的 ThreadLocalMap 仍然持有对该对象的强引用,因此 ThreadLocal 对象不会被垃圾回收器回收,这可能会导致内存泄漏。
  • **弱引用作为键:**如果 ThreadLocal 的键使用弱引用,在我们将 ThreadLocal 的引用设为 null 后,如果垃圾回收器运行,那么 ThreadLocal 对象会被回收,其对应的 Entry 的键变为 null。虽然在下一次调用 set 方法时会清理 null 键,但如果在这期间没有调用 set 或者没有机会执行 ThreadLocalMap 的清理逻辑(例如 get 方法中的清理过程),那么含有 null 键的 Entry 将保留在 ThreadLocalMap 中,从而可能导致内存泄漏。
  • **内存泄漏的根本原因:**不论使用强引用还是弱引用,如果不适当地管理 ThreadLocal 的生命周期,都可能发生内存泄漏。最根本的原因在于 ThreadLocalMap 的生命周期通常与线程相同,如果线程持续运行且不清理过期的 ThreadLocal 实例,即使使用了弱引用,也可能导致内存泄漏。
  • **避免内存泄漏的方法:**为了防止内存泄漏,建议在不再需要某个 ThreadLocal 实例时显式调用其 remove 方法,或者确保在适当的时机让垃圾回收器能够回收不再使用的 ThreadLocal 实例。此外,了解 ThreadLocalMap 的清理机制并适时触发清理也是很重要的。

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

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

相关文章

全栈项目小组【算法赛】题目及解题

题目&#xff1a;全栈项目小组【算法赛】 题目&#xff1a; 解题思路 1.遍历简历信息&#xff1a;我们需要读取所有简历&#xff0c;根据期望薪资和岗位类型进行分类和统计。 2.分类统计&#xff1a;使用哈希表来存储每个薪资下的前端&#xff08;F&#xff09;和后端&#…

【线程】线程的同步

本文重点&#xff1a;理解条件变量和生产者消费者模型 同步是在保证数据安全的情况下&#xff0c;让我们的线程访问资源具有一定的顺序性 条件变量cond 当一个线程互斥地访问某个变量时&#xff0c;它可能发现在其它线程改变状态之前&#xff0c;它什么也做不了&#xff0c;…

window系统DockerDesktop 部署windows容器

目录 参考文献1、安装Docker Desktop1.1 下载安装包1.2 安装教程1.3 异常解决 2、安装windows容器2.1 先启动DockerDesktop 软件界面2.2 检查docker版本2.3 拉取windows镜像 参考文献 windows容器docker中文官网 Docker: windows下跑windows镜像 1、安装Docker Desktop 1.1 …

SSM框架VUE电影售票管理系统开发mysql数据库redis设计java编程计算机网页源码maven项目

一、源码特点 smm VUE电影售票管理系统是一套完善的完整信息管理类型系统&#xff0c;结合SSM框架和VUE、redis完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用ssm框架&#xff08;MVC模式开发&#xff09;&#xff0c;系 统具有完整的源代码和数据库&#…

【C语言零基础入门篇 - 17】:排序算法

文章目录 排序算法排序的基本概念冒泡排序选择排序插入排序 排序算法 排序的基本概念 1、什么是排序&#xff1f; 排序是指把一组数据以某种关系&#xff08;递增或递减&#xff09;按顺序排列起来的一种算法。 例如&#xff1a;数列 8、3、5、6、2、9、1、0、4、7 递增排序…

深入浅出:Eclipse 中配置 Maven 与 Spark 应用开发全指南

Spark 安装配置 1.在 Eclipse 中配置 Maven Eclipse 中默认自带 Maven 插件&#xff0c;但是自带的 Maven 插件不能修改本地仓库&#xff0c;所 以通常我们不使用自带的 Maven &#xff0c;而是使用自己安装的&#xff0c;在 Eclipse 中配置 Maven 的 步骤如下&#xff1a;…

Nature Electronics |无感佩戴的纤维基电子皮肤(柔性半导体器件/柔性健康监测/电子皮肤/柔性传感/纤维器件)

英国剑桥大学Yan Yan Shery Huang课题组,在《Nature Electronics 》上发布了一篇题为“Imperceptible augmentation of living systems with organic bioelectronic fibres”的论文,第一作者为王文宇博士(Wenyu Wang),论文内容如下: 一、 摘要 利用电子技术对人类皮肤和…

0-PCIE串行高速接口架构介绍

随着计算机技术日新月异的发展&#xff0c;对于I/O传输速率的需求愈发提高&#xff0c;PCI总线由于是并行传输&#xff0c;在时钟频率提高之后会带来信号偏移和串扰的问题从而使信号衰减失真&#xff0c;同时在数据传输速率不断提高之后PCI总线还面临着管脚限制&#xff0c;传输…

哈电集团数智化转型新突破:浪潮信息SAP HANA驱动数智升级

浪潮信息SAP HANA一体化解决方案&#xff0c;鼎力推动哈尔滨电气集团有限公司&#xff08;哈电集团&#xff09;取得了数字化转型的非凡成就。该定制化方案不仅促使哈电集团业财一体化程度显著跃升&#xff0c;突破70%大关&#xff0c;更确保了库存管理的绝对精准&#xff0c;库…

【C++前缀和 排序】2171. 拿出最少数目的魔法豆|1748

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode2171. 拿出最少数目的魔法豆 难度分&#xff1a;1748 给定一个 正整数 数组 beans &#xff0c;其中每个整数表示一个袋子里装的魔法豆的数目。 请你从每个袋…

Vue3实现类ChatGPT聊天式流式输出(vue-sse实现)

1. 效果展示 流式输出 直接输出 2. 核心代码 找了一些示例与AI生成的代码&#xff0c;或多或少有些问题&#xff0c;搞了好久&#xff0c;郁闷~&#xff0c;在此记录下 2.1 依赖安装 npm install vue-sse2.2 改写main.ts import VueSSE from vue-sseconst app Vue.cre…

饲料颗粒机全套设备有哪些机器组成

饲料颗粒机全套设备通常包括原料粉碎、混合机、制粒机、冷却器、筛分机、包装机以及配套的电气控制等多个部分组成&#xff1a;1、粉碎机&#xff1a;将各种饲料原料进行清理、去杂、破碎等预处理&#xff0c;确保原料的纯净度和适宜粒度&#xff0c;为后续加工做准备。2、混合…

撤销与恢复的奥秘:设计模式之备忘录模式详解

备忘录模式 &#x1f3af; 备忘录模式&#xff08;Memento Pattern&#xff09;简介 备忘录模式 是一种行为型设计模式&#xff0c;用于保存对象的某一时刻状态&#xff0c;以便稍后可以恢复到该状态&#xff0c;而不破坏对象的封装性。备忘录模式将对象的状态封装在一个独立的…

240922-Conda的在线下载与离线安装

A. 修改路径&#xff08;如果需要&#xff09; 在 conda 中无法直接通过命令指定下载路径。默认情况下&#xff0c;conda 将软件包下载到其缓存目录中&#xff0c;具体位置通常是 ~/miniconda/pkgs 或 ~/anaconda/pkgs&#xff0c;取决于你安装 conda 的路径。 如果你希望将下…

【机器学习】ROC曲线

【机器学习】ROC曲线 1、ROC曲线简介2、ROC曲线和AUC值2.1 ROC曲线2.2 AUC值 3、实验内容3.1 准备数据集3.2 特征提取3.3 数据集划分3.4 模型训练与预测3.5 计算和绘制ROC曲线3.6 绘制混淆矩阵3.7 三分类混淆矩阵 4 源代码4.1 实现ROC二分类4.2 三分类混淆例子 1、ROC曲线简介 …

Qt 注册表操作

一.操作环境 二.注册表查看 1. 搜索注册表打开 2. 注册表查看 例如我想操作 计算机\HKEY_CURRENT_USER\SOFTWARE\winzq\qwert下的内容 三.代码 1. H文件 #ifndef __REGISTER_H__ #define __REGISTER_H__#include <QString> #include <QSettings> #include <Q…

Kotlin 类和属性(五)

导读大纲 1.1 封装行为和数据: 类和属性1.1.1 将数据与类关联并使其可被访问: 属性1.1.2 计算属性,而不是存储其值: 自定义访问器1.1.3 Kotlin 源代码目录和包 1.1 封装行为和数据: 类和属性 与其他面向对象编程语言一样,Kotlin 也提供类的抽象 Kotlin 在这方面的概念您一定不…

UE学习篇ContentExample解读-----------Blueprint_Overview

文章目录 总览描述批次阅览1.1 Blueprint- Hello World1.2 Blueprint- Components1.3 Blueprint- Variables1.4 Blueprint- ConstructionScript1.5 Blueprint- Event Graph1.6 Blueprint- Simple Math1.7 Blueprint- Flow Control 概念总结致谢&#xff1a; 总览描述 打开关卡后…

Golang | Leetcode Golang题解之第430题扁平化多级双向链表

题目&#xff1a; 题解&#xff1a; func dfs(node *Node) (last *Node) {cur : nodefor cur ! nil {next : cur.Next// 如果有子节点&#xff0c;那么首先处理子节点if cur.Child ! nil {childLast : dfs(cur.Child)next cur.Next// 将 node 与 child 相连cur.Next cur.Chi…

超越sora,最新文生视频CogVideoX-5b模型分享

CogVideoX-5B是由智谱 AI 开源的一款先进的文本到视频生成模型&#xff0c;它是 CogVideoX 系列中的更大尺寸版本&#xff0c;旨在提供更高质量的视频生成效果。 CogVideoX-5B 采用了 3D 因果变分自编码器&#xff08;3D causal VAE&#xff09;技术&#xff0c;通过在空间和时…