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

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;逐步讲解线程创建、状态管理、同步机制、并发工具以及新兴的虚拟线程技术。每部…...

Linux实时内核稳定性案例

稳定性问题分析 RT_RUNTIME_SHARE案例死锁问题Linux-rt下卡死之hrtimer分析Linux内核宕机案例 -mmap空指针Linux Hung Task分析过程...

解决 VSCode SSH 连接报错:“REMOTE HOST IDENTIFICATION HAS CHANGED” 的问题

问题描述 在使用 VSCode 通过 SSH 连接远程服务器时&#xff0c;我们可能会遇到类似如下的错误日志&#xff1a; WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! ... Offending ED25519 key in C:\Users\DELL/…...

Spring Boot配置类原理、Spring Boot核心机制理解,以及实现自动装置的底层原理

目的:从底层源码角度分析 Spring Boot 配置类以及自动装载的底层原理 文章目录 1. Spring Boot 配置类实现自动装载1.1 @Configuration注解1.2 @Configuration 注解完成 bean 注入流程图1.3 @ConfigurationProperties注解赋值2. Spring Boot的核心机制:自动装配2.1 @SpringBo…...

淘宝API vs 爬虫:合规获取实时商品数据的成本与效率对比

以下是淘宝 API 和爬虫在合规获取实时商品数据方面的成本与效率对比&#xff1a; 成本对比 淘宝 API 开发成本&#xff1a;需要申请开发者账号并获取 API 权限&#xff0c;部分敏感或高频访问的接口可能需要额外的审核或付费。开发过程中需要按照平台规定进行编程&#xff0c;相…...

01-Canvas-使用fabric初始

fabric官网&#xff1a; https://fabric5.fabricjs.com/demos/ 创建画布并绘制 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…...

CMake简单入门

简介 CMake 是一个开源的跨平台构建系统生成工具&#xff0c;旨在简化和自动化项目的构建过程。它主要用于管理和控制软件构建的过程&#xff0c;特别是在处理复杂的项目结构和多个平台时。CMake 并不直接进行编译或链接&#xff0c;而是生成本地构建系统所需的文件&#xff0…...

树莓派 连接 PlutoSDR 教程

在树莓派5上安装PlutoSDR&#xff08;ADALM-Pluto&#xff09;的驱动程序&#xff0c;主要需要安装相关的库和工具&#xff0c;以便与PlutoSDR通信&#xff0c;比如libiio和libad9361&#xff0c;并确保系统能够识别设备。由于树莓派5运行的是基于Linux的系统&#xff08;通常是…...

【时时三省】(C语言基础)用printf函数输出数据3

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 ( 5 ) e格式符。 用格式声明%e指定以指数形式输出实数。如果不指定输出数据所占的宽度和数字部分的小数位数&#xff0c;许多C编译系统&#xff08;如VisualC&#xff09;会自动给出数字部分…...

Git使用(二)--如何配置 GitHub 远程仓库及本地 Git 环境

在日常的开发过程中&#xff0c;使用版本控制工具 Git 是一个非常重要的技能&#xff0c;特别是对于管理和协作开发。通过 GitHub&#xff0c;我们可以轻松地进行代码版本管理和共享。这篇博客将带您一步步学习如何配置 Git 环境并将本地仓库与 GitHub 远程仓库连接起来。 一、…...

在Pycharm配置conda虚拟环境的Python解释器

〇、前言 今天在配置python解释器时遇到了这样的问题 经过一下午自行摸索、上网搜寻后&#xff0c;终于找到的解决的方案&#xff0c;遂将该方法简要的记录下来&#xff0c;以备后用&#xff0c;并希望能帮助到有同样问题或需求的朋友:) 我所使用的软件的版本如下&#xff0c;假…...

CURL一文通

文章目录 1.什么是curl2.curl可以发送什么请求3.常见curl发http相关请求怎么写4.curl带上的参数分别有什么&#xff0c;可以怎么用5.进阶用法6.常见错误以及学习指导建议 1.什么是curl 是利用URL语法在命令行下工作的开源文件传输工具。尤其被广泛应用的在linux系统下。 2.cu…...

零基础keil:设置注释快捷键

1.打开快捷键设置&#xff1a; 在Keil中&#xff0c;选择菜单栏中的“Settings”&#xff0c;然后选择“Shortcuts”来打开快捷键设置界面。 2.选择注释命令&#xff1a; 在快捷键设置界面中&#xff0c;找到与注释相关的命令&#xff0c;如“Comment Selection”&#xff0…...

Java中关于Optional的 orElse 操作,以及 orElse 与 orElseGet 的区别

文章目录 1. 大概说明2. 详细分析2.1 .orElse 操作2.2 .orElse 的作用&#xff1a;避免空指针异常2.3 为什么要用&#xff1f;2.4 orElseGet如何使用2.5 orElse和orElseGet的区别 1. 大概说明 这篇文章的目的是为了说明&#xff1a; orElse 如何使用orElseGet 如何使用两者的…...

TCP/IP协议中三次握手(Three-way Handshake)与四次挥手(Four-way Wave)

TCP/IP协议中三次握手&#xff08;Three-way Handshake&#xff09;与四次挥手&#xff08;Four-way Wave&#xff09; 一、TCP三次握手&#xff08;Three-way Handshake&#xff09;二、TCP四次挥手&#xff08;Four-way Wave&#xff09;三、常见问题解答总结为什么三次握手不…...

python学智能算法(八)|决策树

【1】引言 前序学习进程中&#xff0c;已经对KNN邻近算法有了探索&#xff0c;相关文章链接为&#xff1a; python学智能算法&#xff08;七&#xff09;|KNN邻近算法-CSDN博客 但KNN邻近算法有一个特点是&#xff1a;它在分类的时候&#xff0c;不能知晓每个类别内事物的具…...

【经验】Orin系列Ubuntu远程桌面:VNC、NoMachine、URDC

1、VNC 1.1 Ubuntu端 1)安装VNC服务器 sudo apt install tigervnc-standalone-server2)安装xfce4 桌面 xfce4 用资源较GNOME ,KDE较少。适合老机器,轻量级桌面。与windows界面环境类似。 sudo apt install xfce4 xfce4-goodies也可以使用其它的桌面系统,可以使用如下命…...

【QT:控件】

目录 控件状态&#xff1a;​编辑 geometry : window frame windowlcon: qrc机制 qrc的使用方式&#xff1a; window opacity cursor font: ToolTip focusPolicy: styleSheet: 按钮类控件&#xff1a; PushButton: 给按钮添加图标&#xff1a; 给按钮添加快捷键…...

Python(最新版)集成开发环境PyCharm下载安装详细教程

Python 下载和安装 1.进入Python官网 Download Python | Python.org&#xff0c;点击Downloads&#xff0c;这里以Windows为例 2.选择下载Python 3.13.2 Windows 64位的版本。注意&#xff1a;不能在Windows 7 或更早的版本上使用。 3.打开文件&#xff0c;会自动出现安装界…...

PyTorch 实现 Conditional DCGAN(条件深度卷积生成对抗网络)进行图像到图像转换的示例代码

以下是一个使用 PyTorch 实现 Conditional DCGAN&#xff08;条件深度卷积生成对抗网络&#xff09;进行图像到图像转换的示例代码。该代码包含训练和可视化部分&#xff0c;假设输入为图片和 4 个工艺参数&#xff0c;根据这些输入生成相应的图片。 1. 导入必要的库 import …...

【BERT和GPT的区别】

BERT采用完形填空&#xff08;Masked Language Modeling, MLM&#xff09;与GPT采用自回归生成&#xff08;Autoregressive Generation&#xff09;的差异&#xff0c;本质源于两者对语言建模的不同哲学导向与技术目标的根本分歧。这种选择不仅塑造了模型的架构特性&#xff0c…...

PTA 7-12 排序

题目描述 给定 n 个&#xff08;长整型范围内的&#xff09;整数&#xff0c;要求输出从小到大排序后的结果。 本题旨在测试各种不同的排序算法在各种数据情况下的表现。各组测试数据特点如下&#xff1a; 数据1&#xff1a;只有1个元素&#xff1b;数据2&#xff1a;11个不…...

uniapp 实现的步进指示器组件

采用 uniapp 实现的一款步进指示器组件&#xff0c;展示业务步骤进度等内容&#xff0c;对外提供“前进”、“后退”方法&#xff0c;让用户可高度自定义所需交互&#xff0c;适配 web、H5、微信小程序&#xff08;其他平台小程序未测试过&#xff0c;可自行尝试&#xff09; 可…...

大模型-提示词调优

什么是提示词 提示词&#xff08;Prompt&#xff09;在大模型应用中扮演着关键角色&#xff0c;它是用户输入给模型的一段文本指令 。简单来说&#xff0c;就是我们向大模型提出问题、请求或描述任务时所使用的文字内容。例如&#xff0c;当我们想让模型写一篇关于春天的散文&a…...

【k8s002】k8s健康检查与故障诊断

k8s健康检查与故障诊断 ‌一、集群状态检查‌ ‌检查节点健康状态‌ kubectl get nodes -o wide # 查看节点状态及基本信息 kubectl describe node <node-name> # 分析节点详细事件&#xff08;如资源不足、网络异常&#xff09; kubectl top nodes …...

统计数字字符个数(信息学奥赛一本通-1129)

【题目描述】 输入一行字符&#xff0c;统计出其中数字字符的个数。 【输入】 一行字符串&#xff0c;总长度不超过255。 【输出】 输出为1行&#xff0c;输出字符串里面数字字符的个数。 【输入样例】 Peking University is set up at 1898. 【输出样例】 4 【输出样例】 #in…...

CentOS 6 YUM源切换成国内yum源

由于 CentOS 6 已于 2020 年 11 月进入 EOL&#xff08;End of Life&#xff09;&#xff0c;官方软件源已不再提供更新&#xff0c;因此你可能会遇到 yum makecache 命令失败的问题。以下是解决该问题的详细步骤&#xff1a; ### 解决方案 1. **备份原有 yum 源文件** bash …...

继承知识点—详细

一&#xff1a;普通写法 package extend_;public class Extends01 {public static void main(String[] args) {Pubil pubil new Pubil();pubil.name"小明";pubil.age18;pubil.testing();pubil.setScore(60);pubil.showInfo();System.out.println("-----------…...

设备管理VTY(Telnet、SSH)

实验目的&#xff1a;物理机远程VTY通过telnet协议登录AR1,ssh协议登录AR2和sw 注意配置Cloud1&#xff1a; 注意&#xff01;&#xff01;博主的物理机VMnet8--IP&#xff1a;192.168.160.1&#xff0c;所以AR1路由0/0/0端口才添加IP&#xff1a;192.168.160.3&#xff0c;每个…...

Linux 中 Git 使用指南:从零开始掌握版本控制

目录 1. 什么是 Git&#xff1f; Git 的核心功能&#xff1a; 2. Git 的安装 Ubuntu/Debian 系统&#xff1a; 验证安装&#xff1a; 3.gitee库 4. Git 的首次配置 配置用户名和邮箱&#xff1a; 查看配置&#xff1a; 5. Git 的基本使用 初始化仓库 添加文件到暂存区…...