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

news2024/9/23 10:54:57

文章目录

  • 【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>() {
        @Override
        protected 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() {
            @Override
            public 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 {
    @Autowired
    private 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 {
    @Autowired
    private 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> {

    @Override
    protected K initialValue() {
        return null;
    }

    @Override
    public void remove() {
        super.remove();
        // 显式调用 remove 方法以确保从 ThreadLocalMap 中删除该条目
    }

    @Override
    public void set(K value) {
        super.set(value);
        // 每次调用 set 时都会自动清理 null 键
    }

    // 其他方法...

    public static void main(String[] args) {
        SafeThreadLocal<String> threadLocal = new SafeThreadLocal<>();

        // 使用 ThreadLocal
        threadLocal.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.coloradmin.cn/o/2157363.html

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

相关文章

如何融合文本信息提高时序预训练模型?

今天小编给大家介绍两篇联合文本和时序数据进行预训练的文章。 UniTime: A Language-Empowered Unified Model for Cross-Domain Time Series Forecasting 文献地址&#xff1a;https://arxiv.org/pdf/2310.09751.pdf 代码地址&#xff1a;https://github.com/liuxu77/UniTim…

【永磁同步电机(PMSM)】 4. 同步旋转坐标系仿真模型

【永磁同步电机&#xff08;PMSM&#xff09;】 4. 同步旋转坐标系仿真模型 1. Clarke 变换的模型与仿真1.1 Clarke 变换1.2 Clarke 变换的仿真模型 2. Park 变换的模型与仿真2.1 Park 变换2.2 Park 变换的仿真模型 3. Simscape标准库变换模块3.1 abc to Alpha-Beta-Zero 模块3…

【COMSOL】1-1 COMSOL6.2软件安装

1.解压COMSOL软件安装包&#xff0c;以管理员身份运行Setup.exe 2.选择简体中文&#xff0c;点击下一步&#xff0c;点击新安装。 3.选择许可证文件 4.自定义安装的位置 5.取消勾选更新&#xff0c;点击下一步 6.若已安装MATLAB则自动识别文件夹&#xff0c;若未安装空着即可&a…

Linux学习笔记(2)

Linux学习笔记&#xff08;2&#xff09; 知识点&#xff1a; 1.打包、压缩——是什么、为什么、怎么做&#xff1f; 什么是打包、压缩&#xff1f; 打包&#xff1a;把文件合并。 压缩&#xff1a;通过一定算法减少体积。 为什么要进行打包、压缩&#xff1f; 打包&…

花园管理系统

基于springbootvue实现的花园管理系统 &#xff08;源码L文ppt&#xff09;4-074 4功能结构 为了更好的去理清本系统整体思路&#xff0c;对该系统以结构图的形式表达出来&#xff0c;设计实现该“花开富贵”花园管理系统的功能结构图如下所示&#xff1a; 图4-1 系统总体结…

【LLM论文日更】| 俄罗斯套娃嵌入模型

论文&#xff1a;https://proceedings.neurips.cc/paper_files/paper/2022/file/c32319f4868da7613d78af9993100e42-Paper-Conference.pdf代码&#xff1a;GitHub - RAIVNLab/MRL: Code repository for the paper - "Matryoshka Representation Learning"机构&#x…

vue3ScrollSeamless滚动如何给子元素添加点击事件:事件委托

页面布局如上截图 下面是方法 function parentClick(e) {if (e.target.tagName A) {router.push({path: /noticeDetails,query: {id: e.target.dataset.eid}});} }使用的时候&#xff0c;可以打印一下方法里面的e&#xff0c;加深理解

Microsoft Edge 五个好用的插件

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏 插件_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 Microsoft Edge 一.安装游览器 ​编辑 二.找到插件商店 1.打开游览器后&#xff0c;点击右上角的设置&#…

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下)

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;上&#xff09; 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器&#xff08;下&#xff09; 文章目录 系列文章目录前言一、装饰器语法6.Builder语法&#xff1a;&#xff08;1&…

室内北斗定位系统常用的几种定位方式

随着科技的不断进步&#xff0c;室内定位技术日益成熟&#xff0c;为人们的日常生活和工作带来了极大的便利。特别是在室内环境中&#xff0c;北斗卫星定位系统通过一系列创新技术&#xff0c;实现了高精度、高可靠的定位服务。接下来就由深圳沧穹科技给大家具体介绍室内北斗定…

OpenCL 学习(2)---- OpenCL Platform 和 Device

目录 OpenCL PlatformOpenCL Device参考代码 OpenCL Platform opencl 支持的 Platform 可以使用 clGetPlatformIDs 函数查询&#xff0c;函数原型如下&#xff1a; clGetPlatformIDs(cl_uint /* num_entries */,cl_platform_id * /* platforms */,cl_uint * …

解锁亚马逊测评自养号防关联新技术

解锁亚马逊测评自养号防关联的新技术主要包括以下几个方面&#xff0c;这些技术旨在提高测评过程的安全性&#xff0c;降低账号被关联的风险&#xff1a; 1. 独立纯净IP技术 独立纯净IP&#xff1a;采用独立、纯净且未受污染的国外IP地址&#xff0c;确保这些IP未被标记或列入…

CSS clip-path 属性的使用

今天记录一个css属性clip-path&#xff0c;首先介绍下这个属性。 clip-path 是CSS中的一个神奇属性&#xff0c;它能够让你像魔术师一样&#xff0c;对网页元素施展“裁剪魔法”——只展示元素的一部分&#xff0c;隐藏其余部分。想象一下&#xff0c;不用依赖图片编辑软件&am…

JavaWeb--纯小白笔记04:Tomcat整合IDEA

IDEA整合Tomcat 1.点击Idea的导航栏里的Run&#xff0c;选择Edit Configurations 2.点击左上角的""&#xff0c;向下翻找到Tomcat Server 选择里面的Local 3.创建一个web工程&#xff0c;点击IDEA的File-->new-->project 然后选择Java Enterprise&#xff0c;…

【网络安全】网络基础第一阶段——第一节:网络协议基础---- OSI与TCP/IP协议

从今天起&#xff0c;我们正式进入第二部分——网络基础。继续学习网络基础、网络协议等相关内容&#x1f31f;&#x1f31f;&#x1f31f; 目录 一、OSI模型 1.1 分层思想 1.2 OSI参考模型 1.3 数据封装与解封装 1.3.1 数据的封装过程 1.3.2 数据的解封装过程 二、TCP/…

实现信创Linux桌面录制成MP4(源码,银河麒麟、统信UOS)

信创国产化已是大势所趋&#xff0c;在国产操作系统上的应用开发的需求越来越多&#xff0c;比如&#xff0c;有客户需要在银河麒麟和统信UOS上实现录制桌面生成一个mp4文件。那么这个要如何实现了&#xff1f; 一. 技术方案 要完成这些功能&#xff0c;具体来说&#xff0c;…

初写MySQL四张表:(4/4)

进度条很喜人&#xff0c;你是否已经修炼到这一步了呢&#xff1f; 初写MySQL四张表:(1/4)-CSDN博客 初写MySQL四张表:(2/4)_数据库表样例-CSDN博客 初写MySQL四张表:(3/4)-CSDN博客 若现在你已经有了前面的基础&#xff0c;那就正式开始吧。 四张表&#xff1a; 这次在实现…

JavaScript 基础 - 第20天_Node.js入门

文章目录 Day01_Node.js入门提前安装软件目录学习目标01.什么是 Node.js目标讲解小结 02.fs模块-读写文件目标讲解小结 03.path模块-路径处理目标讲解小结 04.案例-压缩前端html目标讲解小结 05.案例-压缩前端JS目标讲解小结 06.认识URL中的端口号目标讲解小结 07.http模块-创建…

花生壳、神卓互联等主流内网穿透技术分享

目录 贝锐花生壳 PHTunnel技术 神卓互联WanGooe Tunnel 技术 贝锐花生壳 PHTunnel技术 贝锐花生壳内网穿透服务商&#xff0c;(使用技术&#xff1a;底层采用自研 PHTunnel技术)除了具备无需公网IP&#xff0c;无需搭建专线、3步创建映射等优势&#xff0c;还拥有可靠、稳定的…

JAVA开源项目 图书馆管理系统 计算机毕业设计

本文项目编号 T 044 &#xff0c;文末自助获取源码 \color{red}{T044&#xff0c;文末自助获取源码} T044&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…