当前位置: 首页 > news >正文

Java 多线程编程:提升系统并发处理能力!

多线程是 Java 中实现并发任务执行的关键技术,能够显著提升程序在多核处理器上的性能以及处理多任务的能力。本文面向初级到中级开发者,从多线程的基本定义开始,逐步讲解线程创建、状态管理、同步机制、并发工具以及新兴的虚拟线程技术。每部分都配有详细的代码示例,帮助读者理解并实践 Java 多线程编程。


什么是多线程?

多线程是指在同一程序中同时运行多个执行流,每个执行流称为一个线程。在 Java 中,线程由 Thread 类或其子类表示,每个线程独立执行特定的任务。通过 Thread.currentThread() 方法,可以获取当前正在运行的线程对象及其信息,例如线程名称。多线程的主要优势在于充分利用 CPU 的多核特性,通过并行执行任务提高程序的吞吐量和响应速度。例如,在 Web 服务器中,每个用户请求可以分配一个线程处理,从而实现高并发的请求处理能力。


创建多线程的三种基本方式

Java 提供了多种创建线程的方式,每种方法适用于不同的场景。以下是三种常见实现及其代码示例:

  1. 继承 Thread

    通过继承 Thread 类并重写 run() 方法,可以直接定义线程的行为。调用 start() 方法启动线程后,JVM 会执行 run() 中的逻辑。

    
    public class ThreadDemo {public static void main(String[] args) throws InterruptedException {MyThread thread = new MyThread();thread.start();}
    }class MyThread extends Thread {public void run() {System.out.println("Thread running: " + Thread.currentThread().getName());}
    }
  2. 实现 Runnable 接口

    实现 Runnable 接口并将其传递给 Thread 对象,是一种更灵活的方式。这种方法将任务逻辑与线程对象分离,符合接口优先的设计原则,常用于需要复用任务逻辑的场景。

    public class ThreadDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new MyRunnable());thread.start();}
    }class MyRunnable implements Runnable {public void run() {System.out.println("Runnable running: " + Thread.currentThread().getName());}
    }
    
  3. 使用 CallableFuture

    Callable 接口允许线程执行完成后返回结果,结合 Future 对象可以获取异步任务的返回值。通常与线程池搭配使用,适用于需要计算结果并进一步处理的场景。

    public class ThreadDemo {public static void main(String[] args) throws InterruptedException {ExecutorService executor = Executors.newSingleThreadExecutor();Future<String> future = executor.submit(new MyCallable());System.out.println(future.get()); // 阻塞获取结果executor.shutdown();}
    }class MyCallable implements Callable<String> {public String call() {return "Result from thread: " + Thread.currentThread().getName();}
    }

对于初学者,建议从 Runnable 开始学习,因其简单且易于扩展。中级开发者可以探索 Callable,结合线程池实现更复杂的异步任务。


线程的状态与管理

Java 中的线程在其生命周期中会经历多种状态,这些状态由 JVM 管理并反映线程的当前执行情况:

  • 新建(New):线程对象已创建,但未调用 start() 方法。
  • 运行(Runnable):调用 start() 后,线程进入可运行状态,等待 CPU 调度。
  • 阻塞(Blocked):线程尝试获取锁但被其他线程占用,进入阻塞队列。
  • 等待(Waiting):通过 wait()join() 等方法进入等待状态,需其他线程唤醒。
  • 超时等待(Timed Waiting):通过 sleep()wait(timeout) 设置有限等待时间。
  • 终止(Terminated):线程执行完毕或异常退出。

线程管理涉及以下关键操作:

  • 中断:调用 thread.interrupt() 发送中断信号,线程可通过 isInterrupted() 检查状态并响应。通常用于优雅地终止线程。
  • 守护线程:调用 thread.setDaemon(true) 将线程标记为守护线程。当所有非守护线程结束时,JVM 会退出,无论守护线程是否完成。常用于日志记录或监控等后台任务。

线程同步与锁机制

当多个线程同时访问共享资源时,可能导致数据不一致或竞争条件。Java 提供了多种锁机制来确保线程安全,每种机制适用于不同的并发场景。以下通过一个示例学校布告栏详细展示这些机制。

示例场景

在 Java 多线程环境中,多个学生(读线程)同时查看布告栏,而一个老师(写线程)更新布告栏,这种场景涉及读写并发问题。下面介绍几种 Java 常见的同步机制,并给出相应的代码示例。


1. synchronized(独读独写,不可中断)

synchronized 是 Java 中最基本的同步机制,通过锁住对象或代码块,确保同一时间只有一个线程可以访问受保护的资源。

原理: synchronized 使用对象锁来实现同步。每个对象都有一个与之关联的锁,当线程进入 synchronized 代码块或方法时,它会尝试获取对象的锁。如果获取成功,线程将继续执行;否则,线程将被阻塞,直到其他线程释放锁。

使用场景

老师写布告栏时,学生不能读且排队等待获取锁;学生读取时,老师不能写且排队等待获取锁。

代码示例

class BulletinBoard {private String message = "Initial Message";// 读方法:只有一个线程读取public synchronized String read() {return message;}// 写方法:只有一个线程可以写public synchronized void write(String newMessage) {System.out.println(Thread.currentThread().getName() + " 正在写入布告栏...");message = newMessage;System.out.println(Thread.currentThread().getName() + " 写入完成: " + message);}
}public class SynchronizedExample {public static void main(String[] args) {BulletinBoard board = new BulletinBoard();// 启动多个学生线程(读)for (int i = 0; i < 5; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " 读取到消息: " + board.read());}, "学生" + i).start();}// 启动老师线程(写)new Thread(() -> {board.write("New Announcement");}, "老师").start();}
}

特点

  • synchronized 适合简单场景,但读写都需要加锁,会影响读性能(读操作不能并发)。
  • synchronized 简单但不够灵活,读和写操作互斥,学生之间也不能并发读,效率较低。

2. ReentrantLock(独读独写,可中断)

ReentrantLock 是一个可重入锁,它提供比 synchronized 更灵活的锁机制,比如尝试锁、超时等待、可中断锁等。

原理: ReentrantLock 实现了 Lock 接口,提供了 lock()unlock() 方法来获取和释放锁。它支持可重入性,即同一个线程可以多次获取同一个锁。

使用场景

老师写布告栏时锁定,学生读时也需要锁定,但可以用更细粒度的控制,如尝试获取锁。

代码示例

import java.util.concurrent.locks.ReentrantLock;class BulletinBoardLock {private String message = "Initial Message";private final ReentrantLock lock = new ReentrantLock(); // 创建重入锁public String read() {lock.lock(); // 加锁try {return message;} finally {lock.unlock(); // 释放锁}}public void write(String newMessage) {lock.lock();try {System.out.println(Thread.currentThread().getName() + " 正在写入布告栏...");message = newMessage;System.out.println(Thread.currentThread().getName() + " 写入完成: " + message);} finally {lock.unlock();}}
}public class ReentrantLockExample {public static void main(String[] args) {BulletinBoardLock board = new BulletinBoardLock();for (int i = 0; i < 5; i++) {new Thread(() -> System.out.println(Thread.currentThread().getName() + " 读取到消息: " + board.read()), "学生" + i).start();}new Thread(() -> board.write("Updated Announcement"), "老师").start();}
}

特点

  • ReentrantLock 需要手动加锁、解锁,容易出错。仍然是独占锁读操作不能并发,影响性能。
  • ReentrantLock 比 synchronized 更灵活,但仍未解决读读并发问题,需要手动管理锁。

3. ReentrantReadWriteLock(多读独写)

ReadWriteLock 提供读锁和写锁,允许多个读线程并发访问,但写线程独占资源。适合读多写少的场景。

原理:ReadWriteLock 维护一对锁:读锁和写锁。读锁允许多个线程同时获取,写锁只允许一个线程获取。当线程获取读锁时,其他线程可以继续获取读锁,但不能获取写锁;当线程获取写锁时,其他线程都不能获取读锁或写锁。

使用场景

多个学生可以同时读布告栏,但老师不能写;老师写时,所有学生不能读。

代码示例

import java.util.concurrent.locks.ReentrantReadWriteLock;class BulletinBoardRW {private String message = "Initial Message";private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();// 读操作(多个线程可以并发执行)public String read() {rwLock.readLock().lock();try {return message;} finally {rwLock.readLock().unlock();}}// 写操作(只有一个线程能执行)public void write(String newMessage) {rwLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + " 正在写入布告栏...");message = newMessage;System.out.println(Thread.currentThread().getName() + " 写入完成: " + message);} finally {rwLock.writeLock().unlock();}}
}public class ReadWriteLockExample {public static void main(String[] args) {BulletinBoardRW board = new BulletinBoardRW();for (int i = 0; i < 5; i++) {new Thread(() -> System.out.println(Thread.currentThread().getName() + " 读取到消息: " + board.read()), "学生" + i).start();}new Thread(() -> board.write("Updated Announcement"), "老师").start();}
}

特点

  • 允许多个读线程并发,提高读性能。但写操作会阻塞所有读操作
  • ReadWriteLock 允许多个学生并发读,提高效率,但写操作仍需独占。

4. StampedLock(乐观读独写)

StampedLock 是 Java 8 引入的优化读写锁,支持乐观读(无锁读)性能更高,但使用更复杂。

原理: StampedLock 维护一个邮戳(stamp),用于表示锁的状态。线程在读取共享资源时,可以先尝试获取乐观读邮戳,如果验证通过,则读取成功;否则,线程需要获取悲观读锁。

使用场景

多个学生可以乐观读布告栏(假设内容不常变),老师写时仍需锁定。

代码示例

import java.util.concurrent.locks.StampedLock;class BulletinBoardStamped {private String message = "Initial Message";private final StampedLock lock = new StampedLock();public String read() {long stamp = lock.tryOptimisticRead();String result = message;if (!lock.validate(stamp)) { // 检查是否有写入发生stamp = lock.readLock();try {result = message;} finally {lock.unlockRead(stamp);}}return result;}public void write(String newMessage) {long stamp = lock.writeLock();try {System.out.println(Thread.currentThread().getName() + " 正在写入布告栏...");message = newMessage;System.out.println(Thread.currentThread().getName() + " 写入完成: " + message);} finally {lock.unlockWrite(stamp);}}
}

特点

  • 乐观读锁在没有写入的情况下提高读取效率。
  • 适用于读多写少的场景。

5. volatile(保证单一变量的可见性)

volatile 是 Java 中的关键字,用于保证单一变量的可见性。当一个线程修改了被 volatile 修饰的变量的值,其他线程能够立即看到最新的值。

volatile 关键字会强制线程在修改共享变量后立即将值刷新到主内存,并在其他线程读取该变量时强制它们从主内存读取最新的值。

volatile 确保变量的可见性,但不提供互斥性。适用于只有一个线程写,多个线程读的场景。

使用场景

老师更新布告栏内容,学生直接读取最新内容,无需锁。

class BulletinBoardVolatile {private volatile String content = "初始内容"; // volatile 保证可见性public void write(String newContent) {System.out.println("[老师] 开始更新(volatile)...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}content = newContent;System.out.println("[老师] 已更新内容:" + content);}public String read() {System.out.println(Thread.currentThread().getName() + " 读取内容(volatile):" + content);return content;}
}
public class VolatileExample{public static void main(String[] args) {BulletinBoard board = new BulletinBoard();new Thread(() -> board.write("讲座通知"), "Teacher").start();for (int i = 1; i <= 3; i++) {new Thread(() -> {while (true) { // 持续监听变化board.read();try { Thread.sleep(300); } catch (InterruptedException e) {}}}, "Student-"+i).start();}}}

特点

  • 适用于简单变量的同步,但不能保证原子性(多个变量或复杂逻辑需要加锁)。

6. Semaphore(控制访问数量)

如果需要限制同时访问的读线程数,可以使用 Semaphore

Semaphore 是信号量,用于控制同时访问资源的线程数量。

使用场景

限制同时读布告栏的学生数量(如最多 5个学生),老师写时独占。

import java.util.concurrent.Semaphore;class BulletinBoardSemaphore {private String content = "初始内容";private final Semaphore semaphore = new Semaphore(3); // 允许3个并发读public void write(String newContent) throws InterruptedException {semaphore.acquire(3); // 获取所有许可try {System.out.println("[老师] 开始更新(Semaphore)...");Thread.sleep(1000);content = newContent;System.out.println("[老师] 已更新内容:" + content);} finally {semaphore.release(3);}}public String read() throws InterruptedException {semaphore.acquire();try {System.out.println(Thread.currentThread().getName() + " 开始读取(Semaphore)...");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 读取内容:" + content);return content;} finally {semaphore.release();}}
}public class SemaphoreExpamle{public static void main(String[] args) {BulletinBoardSemaphore board = new BulletinBoardSemaphore();new Thread(() -> {try { board.write("考试注意事项"); } catch (InterruptedException e) { e.printStackTrace(); }}, "Teacher").start();for (int i = 1; i <= 5; i++) {new Thread(() -> {try { board.read(); } catch (InterruptedException e) { e.printStackTrace(); }}, "Student-"+i).start();}}
}

特点

  • 适用于限制并发访问数的场景。

总结

同步机制读性能写性能特点
synchronized简单易用,自动释放锁,但无法中断等待
ReentrantLock可中断、可设置超时,需要手动释放锁
ReadWriteLock读写分离,提高读多写少场景的性能
StampedLock最高支持乐观读,减少锁竞争,适合读远多于写的场景
volatile最高仅保证可见性,不保证原子性,适合简单状态标志
Semaphore可控控制并发线程数量,适合资源池或限流场景

选择合适的机制可以大幅提升系统的并发性能可扩展性

并发发集合 concurrent

在多线程环境下,普通的集合类(如 ArrayListHashMap 等)不是线程安全的。如果多个线程同时修改这些集合,可能会导致数据不一致或抛出 ConcurrentModificationException 异常。为了解决这个问题,Java 提供了 java.util.concurrent 包,其中包含了一系列线程安全的并发集合类。。以下是常见接口及其实现对比:

接口非线程安全线程安全
ListArrayListCopyOnWriteArrayList
MapHashMapConcurrentHashMap
SetHashSet / TreeSetCopyOnWriteArraySet
QueueLinkedListLinkedBlockingQueue

ConcurrentHashMap 是高并发场景下的优选实现,通过分段锁和无锁读取优化了性能,适合需要频繁读写的应用。

 
import java.util.concurrent.*;public class ConcurrentCollectionsExample {public static void main(String[] args) throws InterruptedException {// ConcurrentHashMap 示例ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();concurrentHashMap.put("A", 1);concurrentHashMap.put("B", 2);System.out.println("ConcurrentHashMap: " + concurrentHashMap);// CopyOnWriteArrayList 示例CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();copyOnWriteArrayList.add("X");copyOnWriteArrayList.add("Y");System.out.println("CopyOnWriteArrayList: " + copyOnWriteArrayList);// LinkedBlockingQueue 示例LinkedBlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>();linkedBlockingQueue.put(10);linkedBlockingQueue.put(20);System.out.println("LinkedBlockingQueue: " + linkedBlockingQueue);// 创建一个线程池ExecutorService executorService = Executors.newFixedThreadPool(3);// 使用 ConcurrentHashMap 的示例executorService.submit(() -> {concurrentHashMap.put("C", 3);System.out.println("ConcurrentHashMap (Thread 1): " + concurrentHashMap);});executorService.submit(() -> {concurrentHashMap.get("A");System.out.println("ConcurrentHashMap (Thread 2): " + concurrentHashMap);});// 使用 CopyOnWriteArrayList 的示例executorService.submit(() -> {copyOnWriteArrayList.add("Z");System.out.println("CopyOnWriteArrayList (Thread 3): " + copyOnWriteArrayList);});// 关闭线程池executorService.shutdown();executorService.awaitTermination(1, TimeUnit.MINUTES);// 输出最终结果System.out.println("Final ConcurrentHashMap: " + concurrentHashMap);System.out.println("Final CopyOnWriteArrayList: " + copyOnWriteArrayList);}
}

创建多线程的第四种方式:线程池与 ExecutorService

线程池是一种管理线程的工具,通过复用线程避免频繁创建和销毁的开销,提高资源利用率。ExecutorService 是 Java 提供的线程池接口,支持任务提交、执行管理和结果获取。常见的线程池类型包括固定线程池(newFixedThreadPool)、单线程池(newSingleThreadExecutor)和缓存线程池(newCachedThreadPool)。

以下是一个扩展示例,模拟订单处理并统计完成任务数:

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;public class ThreadPoolDemo {// 用于统计任务完成次数的共享计数器,线程安全private static final AtomicInteger completedTasks = new AtomicInteger(0);// 定义订单处理任务static class OrderTask implements Runnable {private final int orderId;public OrderTask(int orderId) {this.orderId = orderId;}@Overridepublic void run() {try {// 模拟订单处理耗时 1 秒Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + " processed order " + orderId);completedTasks.incrementAndGet(); // 任务完成计数加 1} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}// 主方法,演示线程池用法public static void main(String[] args) throws InterruptedException {// 创建固定大小的线程池,最多 3 个线程ExecutorService executor = Executors.newFixedThreadPool(3);// 提交 5 个订单任务for (int i = 1; i <= 5; i++) {executor.submit(new OrderTask(i));}// 关闭线程池,不接受新任务executor.shutdown();// 等待所有任务完成,最多等待 10 秒boolean finished = executor.awaitTermination(10, TimeUnit.SECONDS);if (finished) {System.out.println("All tasks completed. Total: " + completedTasks.get());} else {System.out.println("Timeout! Tasks completed: " + completedTasks.get());}}
}

结果获取方式

  • 使用 Future:适用于需要同步获取结果的场景。

    Future<Integer> future = executor.submit(() -> {Thread.sleep(1000);return 42;
    });
    System.out.println("Result: " + future.get());
  • 使用 CompletableFuture:支持异步回调,适合复杂任务流。

    CompletableFuture.supplyAsync(() -> {try { Thread.sleep(1000); } catch (Exception e) {}return 42;
    }).thenAccept(result -> System.out.println("Result: " + result));

ThreadLocal:线程隔离

ThreadLocal 是一个线程局部存储工具,为每个线程提供独立的变量副本,避免线程间的数据干扰。它通过维护一个线程到对象映射表实现隔离,常用于 Web 应用中保存用户会话信息或数据库连接等线程私有数据。需要注意,在使用完毕后调用 remove() 方法清理数据,以防止内存泄漏。

以下是一个扩展示例,模拟 Web 请求中的用户上下文管理:

public class ThreadLocalDemo {// 定义 ThreadLocal,存储每个线程的用户 IDprivate static final ThreadLocal<String> userId = new ThreadLocal<>();// 模拟 Web 请求处理的任务static class RequestTask implements Runnable {private final String id;public RequestTask(String id) {this.id = id;}@Overridepublic void run() {// 设置当前线程的用户 IDuserId.set(id);try {// 模拟处理请求System.out.println(Thread.currentThread().getName() + " handling user: " + userId.get());Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 清理 ThreadLocal,避免内存泄漏userId.remove();}}}// 主方法,演示 ThreadLocal 用法public static void main(String[] args) throws InterruptedException {// 创建线程池,模拟多个请求ExecutorService executor = Executors.newFixedThreadPool(2);// 提交两个用户请求executor.submit(new RequestTask("User1"));executor.submit(new RequestTask("User2"));// 关闭线程池并等待executor.shutdown();executor.awaitTermination(5, TimeUnit.SECONDS);}
}

虚拟线程(Project Loom)

虚拟线程是 Java 19 引入的实验性特性(Project Loom),旨在解决传统线程在高并发场景下的资源开销问题。传统线程(平台线程)直接映射到操作系统线程,创建和维护成本高,难以支持大规模并发。而虚拟线程由 JVM 管理,作为轻量级执行单元,依托少量的载体线程运行,适合 IO 密集型任务,例如处理大量网络请求。使用虚拟线程需要启用 --enable-preview 编译和运行参数。

以下是一个扩展示例,模拟高并发请求处理:

import java.util.concurrent.*;public class VirtualThreadDemo {// 定义任务,模拟处理请求static class RequestTask implements Runnable {private final int requestId;public RequestTask(int requestId) {this.requestId = requestId;}@Overridepublic void run() {try {// 模拟 IO 操作(如网络请求)Thread.sleep(1000);System.out.println(Thread.currentThread() + " processed request " + requestId);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}// 主方法,演示虚拟线程public static void main(String[] args) throws InterruptedException {// 使用虚拟线程执行器,每个任务一个虚拟线程try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {// 提交 100 个请求任务for (int i = 1; i <= 100; i++) {executor.submit(new RequestTask(i));}// 等待 2 秒观察结果Thread.sleep(2000);} // try-with-resources 自动关闭执行器System.out.println("All tasks submitted");}
}

总结

  • 初级阶段:学习 Runnablesynchronized,掌握线程创建和基本同步。
  • 中级阶段:深入理解锁机制、线程池和并发集合,探索 CompletableFuture 的异步编程。
  • 进阶方向:关注虚拟线程技术,适应高并发场景的需求。

相关文章:

Java 多线程编程:提升系统并发处理能力!

多线程是 Java 中实现并发任务执行的关键技术&#xff0c;能够显著提升程序在多核处理器上的性能以及处理多任务的能力。本文面向初级到中级开发者&#xff0c;从多线程的基本定义开始&#xff0c;逐步讲解线程创建、状态管理、同步机制、并发工具以及新兴的虚拟线程技术。每部…...

Mininet 的详细设计逻辑

Mininet 是一个轻量级网络仿真工具&#xff0c;其核心目标是在单台物理机上快速构建复杂的虚拟网络拓扑&#xff0c;支持 SDN&#xff08;软件定义网络&#xff09;和传统网络协议的实验与验证。其设计逻辑围绕 虚拟化、模块化 和 灵活性 展开&#xff0c;以下是其详细设计架构…...

原生微信小程序实现导航漫游(Tour)

效果&#xff1a; 小程序实现导航漫游 1、组件 miniprogram/components/tour/index.wxml <!--wxml--> <view class"guide" wx:if"{{showGuide}}"><view style"{{guideStyle}}" class"guide-box"><view class&quo…...

Spring(6)——Spring、Spring Boot 与 Spring MVC 的关系与区别

Spring、Spring Boot 与 Spring MVC 的关系与区别 1. 核心定位 Spring 定位&#xff1a;基础框架&#xff0c;提供 IoC&#xff08;控制反转&#xff09; 和 DI&#xff08;依赖注入&#xff09; 核心功能&#xff0c;管理对象生命周期及依赖关系。功能&#xff1a;支持事务管…...

神聖的綫性代數速成例題2. 行列式的性質

性質 1&#xff1a;行列式與它的轉置行列式相等&#xff1a; 設為行列式&#xff0c;為其轉置行列式&#xff0c;則。 性質 2&#xff1a;交換行列式的兩行 (列)&#xff0c;行列式變號&#xff1a; 若行列式經過交換第行和第行得到行列式&#xff0c;則。 性質 3&#xff…...

ModelScope推理QwQ32B

文章目录 ModelScope推理QwQ32Bmodel_scope下载QwQ32BModelScope 调用QwQ-32B ModelScope推理QwQ32B 以下载 qwq32b 为例子 需要安装的 python 包 transformers4.49.0 accelerate>0.26.0 torch2.4.1 triton3.0.0 safetensors0.4.5可以使用 conda 创建一个虚拟环境安装 cond…...

使用unsloth进行grpo强化学习训练

说明 unsloth框架可以进行各种sft训练&#xff0c;包括lora和grpo训练。我参考官方方法&#xff0c;使用模型Qwen2.5-3B-Instruct和数据集gsm8k&#xff0c;写了一个grpo训练的例子。 代码 这个代码加载模型Qwen2.5-3B-Instruct和数据集gsm8k。训练完成后先保存lora模型然后…...

【c++】【智能指针】shared_ptr底层实现

【c】【智能指针】shared_ptr底层实现 智能指针之前已经写过了&#xff0c;但是考虑到不够深入&#xff0c;应该再分篇写写。 1 shared_ptr 1.1 shared_ptr 是什么 std::shared_ptr是一个类模板&#xff0c;它的对象行为像指针&#xff0c;但是它还能记录有多少个对象共享它…...

python拉取大视频导入deepseek大模型解决方案

使用Python拉取大视频并导入大模型&#xff0c;需要综合考虑数据获取、存储、处理和资源管理&#xff0c;确保高效稳定地处理大视频数据&#xff0c;同时充分利用大模型的性能&#xff0c;以下是分步方案及代码示例&#xff1a; --- 1. 分块下载大视频&#xff08;避免内存溢出…...

【Python】面向对象

编程的两大特点 面向过程&#xff1a;着重于做什么面向对象&#xff08; oop&#xff09;&#xff1a;着重于谁去做 python是面向对象语言&#xff0c;面向对象三大特点&#xff1a;封装、继承、多态 面向对象&#xff1a;便于代码管理&#xff0c;方便迭代更新。 新式类、经…...

leetcode日记(100)填充每个节点的下一个右侧节点指针

和层序遍历差不多的思路&#xff0c;将节点储存在队列里&#xff0c;一边取出节点一边放入取出节点的左右节点&#xff0c;直到队列空。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL), right(NU…...

docker入门篇

使用docker可以很快部署相同的环境,这也是最快的环境构建,接下来就主要对docker中的基础内容进行讲解.Docker 是一个用于开发、交付和运行应用程序的开源平台&#xff0c;它可以让开发者将应用程序及其依赖打包到一个容器中&#xff0c;然后在任何环境中运行这个容器&#xff0…...

python语法

1. 前面先写import导入模块&#xff0c;完整的语法是&#xff1a; [from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名] 语法还可以是&#xff1a; from 模块名 import 功能名 如果import整个模块的话&#xff0c;需要用.功能名()&#xff0c;来用这个功能&#xff…...

Dify使用部署与应用实践

最近在研究AI Agent&#xff0c;发现大家都在用Dify&#xff0c;但Dify部署起来总是面临各种问题&#xff0c;而且我在部署和应用测试过程中也都遇到了&#xff0c;因此记录如下&#xff0c;供大家参考。Dify总体来说比较灵活&#xff0c;扩展性比较强&#xff0c;适合基于它做…...

微信小程序接入DeepSeek模型(火山方舟),并在视图中流式输出

引言&#xff1a; DeepSeek&#xff0c;作为一款先进的自然语言处理模型&#xff0c;以其强大的文本理解和生成能力著称。它能够处理复杂的文本信息&#xff0c;进行深度推理&#xff0c;并快速给出准确的回应。DeepSeek模型支持流式处理&#xff0c;这意味着它可以边计算边输…...

前端性能优化指标及优化方案

前端性能优化的核心目标是 提高页面加载速度、降低交互延迟、减少资源占用。常见的 Web 性能指标包括 LCP、FID、CLS、TTFB、TTI、FCP 等。 关键性能指标&#xff08;Web Vitals&#xff09; 指标优化方案 &#xff08;1&#xff09;LCP&#xff08;Largest Contentful Paint&…...

正则化介绍

简单介绍 正则化是用于控制模型的复杂度&#xff0c;防止模型在训练数据上过度拟合&#xff08;overfitting&#xff09;。正则化通过在模型的损失函数中引入额外的惩罚项&#xff0c;来对模型的参数进行约束&#xff0c;从而降低模型的复杂度。这个额外的惩罚通常与模型参数的…...

AI时代:数字媒体的无限可能

人工智能和数字媒体技术正深刻改变着我们的生活。通过大数据分析、机器学习等技术&#xff0c;人工智能不仅能精准预测用户需求&#xff0c;还能在医疗、金融等多个领域提供高效解决方案。与此同时&#xff0c;数字媒体技术的进步使得信息传播更加迅速和广泛。社会计算作为新兴…...

自动化爬虫drissionpage

自动化爬虫drissionpage官网 自动化测试框架&#xff1a;DrissionPage DrissionPage调用工具汇总 网络爬虫工具比较-DrissionPage、Selenium、Playwright...

禁毒知识竞赛主持稿串词

尊敬的各位领导、各位来宾、参赛选手们&#xff1a;大家好&#xff01; 在市禁毒委员会的领导下&#xff0c;今年我市开展了以“参与禁毒战争&#xff0c;构建和谐社会”为主题的禁毒宣传月活动。为了进一步加强我市禁毒宣传力度&#xff0c;促进社会治安的好转和社会主义物质文…...

【JDK17】Jlink一秒生成精简的JRE

之前介绍了 Java17模块化的JDK&#xff0c;模块化后按需使用Jlink 用于精简生成 JRE 环境&#xff0c;这让快速的开发环境增强了编码的愉悦感。在实际生产环境中使用 mave 进行项目的构建&#xff0c;其次再是精简 JRE 缩小容器镜像体积&#xff0c;为实现一体化的流程&#xf…...

机器学习周报--文献阅读

文章目录 摘要Abstract 1 文章内容1.1 模型结构1.1.1 LSTMAT的结构设置1.1.2 AWPSO算法优化模型 1.2 实验与结果讨论1.2.1 处理缺失数据1.2.2 模型评估指标1.2.3 比较实验1.2.4 消融实验&#xff08;ABLATION EXPERIMENTS&#xff09; 2相关知识2.1 自适应权重粒子群优化&#…...

硬件地址反序?用位操作为LED灯序“纠偏”。反转二进制数即可解决

特别有意思&#xff0c;LED的灯序与其硬件地址刚好相反&#xff0c;没办法直接通过加1实现二进制进位的亮灯操作&#xff0c;查了一些资料说用数组和switch实现&#xff0c;觉得太麻烦了&#xff0c;思索良久&#xff0c;就想到了反转二进制数解决这个问题。 reverse_bits( )是…...

A* floyd算法 bellman-ford

求源点到目标点最短距离 排序的里面要加上与目标点一个预估距离,与dj算法差距只有这儿 预估要小于等于真实的最短距离,吸引力要适当 越接近实际距离越快 #include<bits/stdc.h> using namespace std;// 方向向量&#xff1a;上、右、下、左 const vector<int> …...

【数据挖掘】KL散度(Kullback-Leibler Divergence, KLD)

KL散度&#xff08;Kullback-Leibler Divergence, KLD&#xff09; 是衡量两个概率分布 P 和 Q之间差异的一种非对称度量。它用于描述当使用分布 Q 逼近真实分布 P 时&#xff0c;信息丢失的程度。 KL散度的数学定义 给定两个离散概率分布 P(x)和 Q(x)&#xff0c;它们在相同的…...

Linux shell 进度条

概述 在 Linux Shell 中实现一个简单的进度条可以通过 printf 命令结合特殊字符来实现&#xff0c;以下是一个示例脚本&#xff0c;它模拟了一个从 0% 到 100% 的进度条。 作用 反馈任务进度&#xff1a;让用户直观了解任务执行的进展情况&#xff0c;比如文件拷贝、系统更新…...

ctfshow web刷题记录

RCE 第一题 eval代码执行 &#xff1a; 1、使用system 加通配符过滤 ?csystem("tac%20fl*") ; 2、反字节执行 xxx %20 echo 反字节 3、变量转移 重新定义一个变量 让他代替我们执行 4、伪协议玩法 ?cinclude$_GET[1]?>&1php://filter/readc…...

leetcode日记(101)填充每个节点的下一个右侧节点指针Ⅱ

意料之中有这题&#xff0c;将之前的思路换一下即可&#xff0c;层序遍历的思路将record&#xff08;记录下一个循环的次数&#xff09;手动加减。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL)…...

大语言模型微调和大语言模型应用的区别?

1. 基本概念 微调&#xff08;Fine-tuning&#xff09; 定义&#xff1a;微调是指在预训练大语言模型的基础上&#xff0c;通过在特定领域或任务的数据上进一步训练&#xff0c;从而使模型在该特定任务上表现更优。 目的&#xff1a;适应具体的任务需求&#xff0c;比如法律文…...

Leetcode-131.Palindrome Partitioning [C++][Java]

目录 一、题目描述 二、解题思路 【C】 【Java】 Leetcode-131.Palindrome Partitioninghttps://leetcode.com/problems/palindrome-partitioning/description/131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09;131. 分割回文串 - 给你一个字符串 s&#xff0c;请你…...

DeepSeek:开启机器人智能化的革命性突破

引言 在2025年全球机器人产业格局中&#xff0c;中国AI公司深度求索&#xff08;DeepSeek&#xff09;凭借开源机器人智能控制系统DeepSeek-R1&#xff0c;正在掀起一场从底层算法到应用生态的技术革命。不同于传统机器人依赖预设程序的局限&#xff0c;DeepSeek通过深度推理能…...

解决load()文件报错zipfile.BadZipFile: File is not a zip file

报错如下图&#xff1a; 有可能是资源没有关闭造成了错误&#xff0c;这个网上已经有很多解决方案了&#xff0c;大家可自行查阅。 如果你在别的地方都没有找到解决问题&#xff0c;那么可能是以下这种情况。 1、描述 我在服务器上的代码load()加载文件时&#xff0c;出现了…...

【Tools】Visual Studio Code安装保姆级教程(2025版)

00. 目录 文章目录 00. 目录01. Visual Studio Code概述02. Visual Studio Code下载03. Visual Studio Code安装04. Visual Studio Code配置05. 附录 01. Visual Studio Code概述 Visual Studio Code&#xff08;简称 VS Code&#xff09;是由微软开发的一款免费、开源且跨平台…...

Python库安装报错解决思路以及机器学习环境配置详细方案

文章目录 概要第三方库gdalmahotasgraphviznltk-datalazypredictscikit-surprisenb_extensions 机器学习GPU-torch安装torch_geometric安装ubuntu安装显卡驱动dlib安装torch-cluster、torch-scatter、torch-sparse和torch-geometricYOLOapextensorflow-gpu Python && P…...

ETIMEDOUT 网络超时问题

根据日志显示&#xff0c;你遇到的 ​**ETIMEDOUT 网络超时问题** 是由于 npm 无法连接到企业内部的 Nexus 仓库&#xff08;http://192.168.55.12:8001&#xff09;导致的。以下是具体原因和解决方案&#xff1a; 一、问题根源 ​Nexus 仓库不可达 日志中所有依赖包均尝试从 h…...

superset部署记录

具备网络条件的&#xff0c;完全可以一键部署&#xff0c;不需要折腾。网络条件不具备时&#xff0c;部署记录留存备查。 1、正常模式 详细介绍参考&#xff1a;【开源项目推荐】Apache Superset——最优秀的开源数据可视化与数据探索平台-腾讯云开发者社区-腾讯云 (tencent.c…...

todolist docker 小工具

参考链接 前排提示 没有中文&#xff0c;可使用浏览器 翻译 前提 安装docker安装docker-compose 下载仓库 git clone https://github.com/JordanKnott/taskcafe进行安装 cd taskcafe docker-compose -p taskcafe up -d服务启动后会监听在 3333 端口上&#xff0c;通过浏览器…...

PowerToys:解锁Windows生产力的终极武器

欢迎来到涛涛聊AI。今天想着把win键和加号的组合键映射为win键和Q键盘。经过搜索发现PowerToys。 在数字化办公的浪潮中&#xff0c;效率是职场人永恒的追求。微软推出的 PowerToys 作为Windows官方系统强化工具&#xff0c;凭借其强大的功能和开源免费的特性&#xff0c;已成为…...

内存管理:

我们今天来学习一下内存管理&#xff1a; 1. 内存分布&#xff1a; 我们先来看一下我们下面的图片&#xff1a; 这个就是我们的内存&#xff0c;我们的内存分为栈区&#xff0c;堆区&#xff0c;静态区&#xff0c;常量区&#xff1b; 我们的函数栈帧开辟消耗的内存就是我们…...

ElementUI 表格中插入图片缩略图,鼠标悬停显示大图

如何在 ElementUI 的表格组件 Table 中插入图片缩略图&#xff0c;通过鼠标悬停显示大图&#xff1f;介绍以下2种方式&#xff1a; 方法1&#xff1a;直接在模板元素中插入 <template><el-table :data"tableData"><el-table-column label"图片…...

从被动响应到主动预见:智能可观测性技术的变革与实践

思维导图 一、引言 &#x1f303; 想象一下&#xff0c;在一个深夜 &#x1f319;&#xff0c;你的关键业务系统突然出现故障 &#x1f6a8;。传统情况下&#xff0c;你可能会收到大量不相关的告警 &#x1f4f1;&#x1f4ac;&#x1f4ac;&#x1f4ac;&#xff0c;然后花费…...

【Linux】五种 IO 模型与非阻塞 IO

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; 重新理解 IO&#x1f98b; 为什么说网络问题的本质是 I/O 问题&#xff1f;&#x1f380; 从数据流动看网络通信&#x1f380; 网络 I/O 的瓶颈 &#x1f…...

从零开始开发纯血鸿蒙应用之无框截图

从零开始开发纯血鸿蒙应用 〇、前言二、元素定位1、理论依据2、使用指导 三、认识 ComponentSnapshot1、get 方法2、获取 ComponentSnapshot 实例 四、实现组件截图1、掌握图片编码能力2、保存到图库3、实现组件截图 〇、前言 截图&#xff0c;或者说截屏&#xff0c;已经是每…...

【商城实战(36)】UniApp性能飞升秘籍:从渲染到编译的深度优化

【商城实战】专栏重磅来袭&#xff01;这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建&#xff0c;运用 uniapp、Element Plus、SpringBoot 搭建商城框架&#xff0c;到用户、商品、订单等核心模块开发&#xff0c;再到性能优化、安全加固、多端适配&#xf…...

无人自助空间智能管理系统解决方案(深度优化版)

无人自助空间智能管理系统解决方案&#xff08;深度优化版&#xff09; 一、行业痛点与系统价值 传统管理依赖人工&#xff1a; 人工管理模式下&#xff0c;易出现人为失误&#xff0c;如计费错误、资源分配不当等。同时&#xff0c;人工操作效率低下&#xff0c;在高峰时段…...

pycharm配置镜像源【pycharm最新版(23.2.5及以上)方法】

经常遇到pycharm中无法安装或者安装慢的问题&#xff0c;纠结了好久&#xff0c;终于找到这个解决办法了。 为什么要配置镜像源&#xff1a; 因为Python的包管理工具pip一般从PyPI&#xff08;Python Package Index&#xff09;下载安装包&#xff0c;但是PyPI位于国外&#x…...

探索ima.copilot:个人知识库搭建的AI新利器

在信息爆炸的时代&#xff0c;知识的积累与管理成为了个人发展的关键。面对海量的科研文献、工作资料和各类信息&#xff0c;如何高效地构建属于自己的知识体系&#xff0c;是许多人面临的挑战。ima.copilot这款AI工具的出现&#xff0c;为解决这一难题提供了新的思路。它凭借强…...

向量数据库技术系列五-Weaviate介绍

一、前言 Weaviate 是由德国公司 SeMI Technologies 开发的开源向量搜索引擎数据库。它结合了向量搜索和图数据库技术&#xff0c;旨在为 AI 应用提供高效的数据存储和检索能力。具有以下的特点&#xff1a; 高性能向量搜索 Weaviate 支持高效的向量索引和近似最近邻&#x…...

1.Qt SDK 的下载和安装

1Qt 下载官⽹&#xff1a; http://download.qt.io/archive/qt/ 2版本自行选择 3下载对应版本的.exe文件 4下载包下载完成 5双击.exe文件&#xff0c;默认下一步&#xff0c;要注册一个qt的账户 6记住程序安装的位置&#xff0c;后面要配置环境变量 7勾3个&#xff08;组件自行…...

光场中的核心概念:Macro Pixel与SAI的深度解析与实例应用

一、概念详解&#xff1a;从硬件到算法的核心要素 Macro Pixel&#xff08;宏像素&#xff09; Macro Pixel是光场相机的硬件核心单元&#xff0c;由微透镜阵列覆盖的一组传感器子像素构成。每个微透镜对应一个宏像素&#xff0c;其子像素分别记录通过该微透镜不同区域的光线方…...