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

Java大师成长计划之第15天:Java线程基础

📢 友情提示:

本文由银河易创AI(https://ai.eaigx.com)平台gpt-4o-mini模型辅助创作完成,旨在提供灵感参考与技术分享,文中关键数据、代码与结论建议通过官方渠道验证。

在现代软件开发中,多线程编程是实现高性能应用的重要手段。Java作为一种具有强大并发能力的语言,提供了丰富的线程操作API。在本篇文章中,我们将深入探讨Java线程的创建、生命周期及相关概念,帮助你在并发编程的道路上更进一步。

一. 什么是线程?

线程是操作系统调度的基本单位,它是进程内的一个独立执行单元。一个进程可以包含多个线程,这些线程共享同一进程的资源(如内存、文件句柄等),但每个线程有自己的寄存器、堆栈等私有资源。由于线程之间共享内存,它们的通信和数据共享比进程之间更为高效,但也因此更容易出现并发问题。

1.1 线程与进程的关系

进程是系统进行资源分配和调度的基本单位,而线程是程序执行的最小单位。一个进程至少包含一个线程,即主线程。进程间的通信通常使用IPC(Inter Process Communication)技术,如管道、消息队列等,而线程间的通信相对简单,通常通过共享内存、锁机制等方式实现。

线程的优势:
  • 提高程序的响应能力:通过将不同的任务分配到多个线程上,可以提高程序的并发能力,例如在一个Web服务器中,可以为每个客户端请求分配一个线程,从而提高系统的并发性能。
  • 资源共享:线程共享进程的内存和文件句柄等资源,这使得线程之间的通信更加高效,而进程间则需要通过IPC机制来交换数据。
  • 减少上下文切换开销:相比进程,线程的创建和销毁开销较小,且线程之间的上下文切换速度比进程切换更快。
线程的缺点:
  • 线程间的同步问题:多个线程共享同一块内存区域,可能会导致数据不一致的问题。为了保证线程安全,通常需要采用同步机制(如sychronized关键字、Lock接口等)来避免数据冲突。
  • 死锁问题:当多个线程互相等待对方持有的锁时,可能会导致死锁,从而使程序无法继续执行。为此,避免死锁和设计合理的锁策略成为并发编程的一个重要问题。

1.2 线程的分类

线程通常分为以下几类:

  • 用户级线程:由应用程序创建和控制,操作系统并不直接管理这些线程。
  • 内核级线程:由操作系统内核管理,操作系统直接调度执行。Java的线程通常是内核级线程,操作系统会为其分配CPU时间。
  • 守护线程:守护线程是为其他线程提供服务的线程,它通常用于后台任务。Java中,像垃圾回收器(GC)就是通过守护线程来运行的。当所有非守护线程结束时,守护线程会自动退出。

1.3 线程的重要性

随着多核处理器的普及,传统的单线程处理方式已经无法满足高并发应用的需求。在这种背景下,Java的多线程机制显得尤为重要。通过合理的线程管理,我们能够有效提高应用程序的吞吐量和响应时间,为用户提供更流畅的使用体验。


二. Java中的线程创建

在Java中,线程的创建和管理是多线程编程的核心。Java为我们提供了多种方式来创建线程,以下是三种常见的方法:

2.1 继承Thread

通过继承Thread类并重写run()方法来定义线程的执行逻辑。继承Thread类创建线程的方式相对简单,但有一个限制:由于Java单继承的特性,类只能继承一个类,因此,如果需要继承其他类时无法再继承Thread类。

class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running.");}
}public class ThreadDemo {public static void main(String[] args) {MyThread thread = new MyThread(); // 创建线程实例thread.start(); // 启动线程}
}

在这个例子中,我们定义了一个继承Thread类的MyThread类,并重写了run()方法。start()方法是启动线程的关键,它会调用run()方法,执行线程的任务。

注意:调用run()方法并不会启动线程。若直接调用run()方法,只会在当前线程中执行run()方法中的代码,而不会在新线程中执行。只有调用start()方法,才会创建一个新的线程。

2.2 实现Runnable接口

另一种创建线程的方式是通过实现Runnable接口。这种方式相较于继承Thread类有一个重要的优点:它支持多个线程共享同一个资源,适合多个线程执行相同的任务。

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Runnable thread is running.");}
}public class RunnableDemo {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable); // 创建线程thread.start(); // 启动线程}
}

这种方式不仅能够实现多线程,还能够让多个线程共享同一个Runnable对象。通过实现Runnable接口,我们可以使得线程的任务更加灵活,因为一个线程类可以继承其他类,同时实现多个接口。

2.3 使用Executor框架

从Java 5开始,java.util.concurrent包引入了Executor框架,它提供了一种更为简洁、灵活的方式来管理线程池。线程池可以有效地避免频繁创建和销毁线程的开销,尤其在高并发场景下,线程池显得尤为重要。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorDemo {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2); // 创建一个固定大小的线程池// 提交任务到线程池executor.submit(() -> {System.out.println("Task 1 is running.");});executor.submit(() -> {System.out.println("Task 2 is running.");});executor.shutdown(); // 关闭线程池}
}

在这个例子中,我们使用Executors.newFixedThreadPool(2)创建了一个包含两个线程的线程池。通过submit()方法提交任务,线程池会管理线程的生命周期并执行任务。使用线程池能够有效减少线程创建和销毁的开销,同时避免资源浪费。

ExecutorService接口提供了许多方法,例如submit()invokeAll()等,可以帮助我们更高效地管理线程池中的线程。

2.4 使用CallableFuture

在某些情况下,我们希望能够获取线程执行的结果,这时可以使用Callable接口和Future类。与Runnable不同,Callable可以返回一个结果并且允许抛出异常。Future则可以用来获取线程的执行结果或者取消线程的执行。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executor = Executors.newCachedThreadPool();Callable<Integer> task = () -> {return 42; // 任务返回结果};Future<Integer> future = executor.submit(task); // 提交任务Integer result = future.get(); // 获取任务结果System.out.println("Task result: " + result);executor.shutdown(); // 关闭线程池}
}

在这个例子中,Callable任务返回一个整数,我们使用Future.get()方法获取线程的执行结果。如果线程执行异常,get()方法会抛出ExecutionException


通过以上几种方式,我们可以在Java中灵活地创建和管理线程。每种方法都有其特定的应用场景,开发者需要根据实际需求选择合适的线程创建方式。随着多线程编程的深入,你将发现线程池等高级特性能够显著提高应用程序的性能和可扩展性。

三. Java线程的生命周期

线程生命周期指的是线程从创建到结束的整个过程。Java中的线程具有不同的状态,线程的状态决定了它在运行时所处的阶段。了解线程的生命周期和各个状态之间的转换,对于编写高效的并发程序至关重要。下面我们将详细介绍Java线程的生命周期,并且解析每个线程状态的含义。

3.1 线程生命周期的各个状态

Java中的线程有五种主要的状态,每个线程在生命周期中可能会处于这五种状态中的任何一种。线程状态之间的转变是由Java虚拟机(JVM)和操作系统的调度机制控制的。

  1. 新建状态(New)
  2. 就绪状态(Runnable)
  3. 运行状态(Running)
  4. 阻塞状态(Blocked)
  5. 等待状态(Waiting)
  6. 超时等待状态(Timed Waiting)
  7. 死亡状态(Terminated)

在这些状态之间,线程的状态会随着线程生命周期的推进发生相互转换。我们可以通过Thread类的方法来观察线程的状态,例如getState()方法。

3.2 线程状态的详细介绍

3.2.1 新建状态(New)

当我们创建一个线程对象,但尚未调用start()方法时,线程处于新建状态。此时,线程并没有开始执行,所有的初始化工作已经完成,但还没有分配CPU资源。

  • 线程转换条件:线程从新建状态进入就绪状态的条件是调用了start()方法。
Thread thread = new Thread(() -> {System.out.println("Thread is running.");
});
System.out.println(thread.getState());  // 输出:NEW
thread.start(); // 线程从NEW状态变为RUNNABLE

3.2.2 就绪状态(Runnable)

当线程调用了start()方法后,线程进入就绪状态。此时线程已经准备好,等待操作系统调度器为其分配CPU时间片。需要注意的是,在Java中,“就绪状态”和“运行状态”有时被称为“可运行状态”,它们的区别仅在于线程是否获得CPU时间片。

  • 线程转换条件:线程处于就绪状态时,操作系统调度器会选择合适的线程进行运行。因此,它会在就绪状态和运行状态之间不断切换。
Thread thread = new Thread(() -> {System.out.println("Thread is running.");
});
thread.start(); // 线程从NEW状态变为RUNNABLE状态

线程从就绪状态进入运行状态的前提是操作系统的线程调度程序为它分配了CPU时间片。

3.2.3 运行状态(Running)

当线程获得操作系统的CPU时间片后,它将进入运行状态。此时,线程开始执行run()方法中的代码。线程运行时,CPU会执行线程的指令。多个线程同时运行时,操作系统会进行线程调度,根据调度算法来分配CPU资源。

  • 线程转换条件:线程从就绪状态进入运行状态,或者在线程正在运行时,操作系统的调度器重新分配CPU时间片。
Thread thread = new Thread(() -> {System.out.println("Thread is running.");
});
thread.start(); // 线程进入RUNNABLE状态,操作系统开始调度并执行该线程

当线程完成run()方法中的代码或者被操作系统中断时,它会离开运行状态,进入阻塞或等待状态,或者直接进入死亡状态。

3.2.4 阻塞状态(Blocked)

阻塞状态通常发生在一个线程试图访问被其他线程持有的同步锁(如sychronized块或Lock对象)时。此时,线程被挂起,直到它能够获取到需要的锁。阻塞状态下的线程不消耗CPU时间,它们处于等待锁的状态。

  • 线程转换条件:当一个线程的执行遇到阻塞条件(如锁竞争),它就会进入阻塞状态。此时,线程不再参与操作系统的调度,直到它获取到所需的资源。
public class BlockedStateExample {private static final Object lock = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock) {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("Thread 2 is running.");}});thread1.start();thread2.start();}
}

在上述代码中,thread2试图获取lock对象的锁,但thread1正在持有锁。因此,thread2进入了阻塞状态,直到thread1释放锁。

3.2.5 等待状态(Waiting)

当一个线程调用Object.wait()Thread.join()LockSupport.park()等方法时,它会进入等待状态。在等待状态下,线程不占用CPU时间,直到收到其他线程的通知或者中断信号。

  • 线程转换条件:线程在等待状态时,必须有其他线程通过notify()notifyAll()interrupt()等方法唤醒或中断它,才能重新进入就绪状态。
public class WaitingStateExample {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {try {Thread.sleep(1000);synchronized (this) {wait(); // 线程进入WAITING状态}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();Thread.sleep(500); // 确保线程已经进入等待状态thread.interrupt(); // 中断线程,线程会离开WAITING状态}
}

线程进入等待状态后,必须经过其他线程的操作才能继续执行。

3.2.6 超时等待状态(Timed Waiting)

当一个线程调用如Thread.sleep(millis)Object.wait(millis)Thread.join(millis)等带有时间参数的方法时,线程会进入超时等待状态。在这种状态下,线程会等待一段时间,超时后自动回到就绪状态。

  • 线程转换条件:在超时等待状态下,线程等待指定时间后会自动回到就绪状态,或者在等待过程中被其他线程唤醒。
public class TimedWaitingStateExample {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {try {Thread.sleep(2000); // 线程进入TIMED_WAITING状态,等待2秒System.out.println("Thread has woken up.");} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
}

在线程执行Thread.sleep()时,线程会进入超时等待状态,超时后线程自动进入就绪状态。

3.2.7 死亡状态(Terminated)

当线程的run()方法执行完成,或者线程因异常终止时,线程进入死亡状态。在死亡状态下,线程无法重新启动。

  • 线程转换条件:线程在run()方法执行完毕或由于异常而终止时进入死亡状态,无法再恢复为活动状态。
Thread thread = new Thread(() -> {System.out.println("Thread is running.");
});thread.start();

一旦线程的执行结束,或者遇到异常未处理,线程便进入死亡状态,无法再调用start()方法。


3.3 线程状态转换图

线程在生命周期中会经历多个状态,并根据不同的条件转换。以下是一个简化的线程状态图:

   +-----------+|   新建    | --------------------++-----------+                    ||                         || start()               ||                         |+-----------+                    ||  就绪    | <-----------------++-----------+                    ||                         || run()                  ||                         |+-----------+                    ||  运行    | ------------+      |+-----------+            |      ||                 +-----+ ||                 | 等待 | ||                 +-----+ ||                       |   ||                       |   ||             +---------+ ||             |  阻塞   | ||             +---------+ ||                       |   |+-----------------------+   ||+----------+---------+|        死亡       |+-------------------+

3.4 总结

理解线程的生命周期和各种线程状态是编写高效且稳定的多线程程序的基础。在并发编程中,线程的调度和资源管理是复杂且关键的。通过正确地控制线程的状态转换,可以有效避免常见的并发问题,如死锁、资源竞争等。

在实际开发中,掌握线程的生命周期,合理运用同步机制和线程池等工具,将有助于提升应用程序的性能和稳定性。

四. 线程的安全性

线程安全性是多线程编程中最为重要的概念之一。由于多个线程可能会同时访问共享的资源,这就导致了线程安全问题。线程安全的意思是,当多个线程并发访问同一资源时,不会发生数据竞争或不可预期的行为。为了确保线程安全,开发者需要采取适当的措施来保证共享资源的正确访问。

4.1 线程安全问题的根源

线程安全问题通常出现在多个线程同时访问共享资源(如内存、变量、对象等)时,特别是当多个线程需要对同一资源进行读写操作时,若没有适当的同步机制,容易出现以下几种问题:

  • 竞态条件(Race Condition):当两个或更多线程并发执行时,如果它们对同一资源进行读写操作,而且这些操作不是原子性的,就可能导致数据的不一致。例如,线程A和线程B同时读取一个共享变量并尝试修改它,最终的结果可能不是预期的。

  • 死锁(Deadlock):当多个线程互相等待对方释放资源时,会导致所有线程都无法继续执行。这种情况通常发生在两个或多个线程在没有适当的控制下相互持有锁时。

  • 资源饥饿(Starvation):当线程永远无法获得所需的资源而导致的状态,通常是因为系统中某些线程总是优先获得执行的机会,而其他线程始终没有机会执行。

为了避免这些问题,我们需要使用线程同步技术来确保对共享资源的访问是有序的。

4.2 线程同步技术

4.2.1 使用synchronized关键字

synchronized是Java中最常用的同步机制,它可以修饰方法或代码块,保证同一时刻只有一个线程可以访问该代码块或方法。

1. 同步方法

在方法声明上使用synchronized关键字,可以让一个线程在执行该方法时获得该对象的锁,其他线程只能等待当前线程执行完毕。

public class Counter {private int count = 0;// 使用synchronized关键字来确保线程安全public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}public class SynchronizedExample {public static void main(String[] args) {Counter counter = new Counter();// 创建多个线程来进行并发操作for (int i = 0; i < 1000; i++) {new Thread(() -> {counter.increment();}).start();}}
}

在上面的代码中,increment()getCount()方法都是使用synchronized修饰的,保证了在任一时刻只有一个线程能够进入这两个方法,从而避免了数据不一致的情况。

2. 同步代码块

除了修饰整个方法,synchronized还可以修饰代码块,这样可以将同步控制的范围缩小到关键代码部分,从而提高性能。

public class Counter {private int count = 0;public void increment() {synchronized (this) { // 使用同步代码块count++;}}public int getCount() {return count;}
}

在这个例子中,只有count++操作是被同步的,而其他操作不会受到影响。这样可以减少同步开销,提高程序的效率。

4.2.2 使用Lock接口

Java 5引入了java.util.concurrent.locks.Lock接口,它提供了比synchronized更加灵活和高效的锁机制。Lock接口的实现类包括ReentrantLockReentrantReadWriteLock等。与synchronized相比,Lock提供了更细粒度的控制,例如尝试获取锁、定时获取锁、锁的公平性等。

1. 使用ReentrantLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock(); // 获取锁try {count++;} finally {lock.unlock(); // 释放锁}}public int getCount() {return count;}
}

在这个例子中,我们使用ReentrantLock来代替synchronized,并且手动管理锁的获取和释放。这样,代码执行时能够确保线程安全,并且可以避免死锁的风险。

2. 公平锁

ReentrantLock支持公平锁和非公平锁。公平锁指的是锁的获取遵循先到先得的原则,而非公平锁则可能让等待时间较长的线程被跳过。默认情况下,ReentrantLock是非公平的,但可以通过构造函数显式指定公平锁。

Lock lock = new ReentrantLock(true); // 公平锁
4.2.3 使用Atomic

Java的java.util.concurrent.atomic包提供了一些原子类(如AtomicIntegerAtomicLongAtomicReference等),它们通过CAS(Compare-And-Swap)算法实现了对单一变量的原子操作。通过这些类,多个线程可以并发地对同一变量进行操作,而不需要显式的同步。

import java.util.concurrent.atomic.AtomicInteger;public class Counter {private AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet(); // 使用原子操作}public int getCount() {return count.get();}
}

在这个例子中,AtomicInteger提供了线程安全的递增操作,避免了手动使用synchronized进行同步的复杂性。

4.2.4 使用volatile关键字

volatile是Java中的一个轻量级同步机制,确保线程对共享变量的读取和写入是直接从主内存中进行,而不是从线程的工作内存中缓存。volatile常用于标志位的共享,以确保多个线程之间的数据一致性。

public class SharedFlag {private volatile boolean flag = false;public void setFlagTrue() {flag = true;}public boolean checkFlag() {return flag;}
}

volatile关键字不适合用于复杂的数据共享场景(例如递增、递减等),但对于简单的标志位,使用volatile可以避免不必要的同步开销。

4.3 线程安全的容器和集合

Java还提供了一些线程安全的集合类,它们可以帮助开发者在多线程环境下安全地操作集合。这些集合类大多位于java.util.concurrent包中,如ConcurrentHashMapCopyOnWriteArrayList等。

4.3.1 ConcurrentHashMap

ConcurrentHashMap是一个线程安全的哈希表,它在设计上将整个哈希表划分为多个段,每个段可以独立进行锁操作,从而提高了并发性。与Hashtable相比,ConcurrentHashMap在并发场景下性能更好。

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();map.put("apple", 1);map.put("banana", 2);// 线程安全的操作System.out.println(map.get("apple"));}
}
4.3.2 CopyOnWriteArrayList

CopyOnWriteArrayList是线程安全的List实现,它的主要特点是每次修改集合时,都会复制一个新的数组,因此它适用于读多写少的场景。

import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();list.add("apple");list.add("banana");// 线程安全的操作System.out.println(list.get(0));}
}

CopyOnWriteArrayList特别适合于读操作非常频繁,而写操作相对较少的场景,避免了多线程访问时的竞争问题。

4.4 死锁避免与调度

死锁是多线程程序中最常见的问题之一。死锁通常发生在多个线程同时持有资源并互相等待对方释放资源的情况下。为避免死锁,可以采取以下措施:

  • 避免嵌套锁:尽量避免线程在持有一个锁的情况下,再去请求其他锁。
  • 加锁顺序:所有线程获取锁的顺序必须相同,以避免死锁的发生。
  • 使用tryLock()ReentrantLock提供了tryLock()方法,它允许尝试获取锁,如果未能成功获取锁,可以选择继续执行或尝试其他操作,避免线程长时间等待。

4.5 总结

线程安全是并发编程中的关键问题,保证线程安全的策略有很多种,包括使用synchronized关键字、Lock接口、原子操作类以及线程安全的容器等。通过选择适合的同步工具,我们可以有效避免并发问题,如竞态条件、死锁等,确保程序在高并发环境下的稳定性和正确性。

线程安全不仅仅是一个技术问题,还是设计问题。在设计并发程序时,除了选择合适的同步机制外,还应合理考虑程序的结构与架构,确保系统在并发高效的同时能够避免潜在的并发错误。

五. 结语

经过本篇文章的学习,我们对Java线程的创建和生命周期有了更深入的理解。掌握线程的相关概念和操作,对于提升应用程序的性能和响应能力至关重要。希望你能在实际开发中灵活运用这些知识,成为一名优秀的Java开发者。

相关文章:

Java大师成长计划之第15天:Java线程基础

&#x1f4e2; 友情提示&#xff1a; 本文由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;平台gpt-4o-mini模型辅助创作完成&#xff0c;旨在提供灵感参考与技术分享&#xff0c;文中关键数据、代码与结论建议通过官方渠道验证。 在现代软件开发中&#xff0c;多线程…...

中小企业设备预测性维护:从技术原理到中讯烛龙实践落地指南

在工业 4.0 与智能制造浪潮的推动下&#xff0c;中小企业正面临设备管理模式的深刻变革。传统的事后维修与预防性维护策略&#xff0c;因缺乏数据驱动与智能决策能力&#xff0c;已难以满足企业降本增效的核心诉求。据 Gartner 统计&#xff0c;非计划停机导致的生产损失平均每…...

mysql 复习

mysql定义与架构 数据库是按照数据结构来组织、存储和管理数据的仓库&#xff0c;方便我们增删查改。MySQL有客户端和服务器端&#xff0c;基于网络服务的&#xff0c;3306端口处于监听状态。 数据库的存储介质有以下两种&#xff1a; 磁盘&#xff0c;比如MySQL就是一种磁盘…...

高低比率策略

本策略的核心在于运用技术指标结合基本规则进行交易决策&#xff0c;旨在通过高低比率策略捕捉市场的超买和超卖信号&#xff0c;以此指导交易行为。 一、交易逻辑思路 1. 指标计算&#xff1a; - 本策略首先通过EMA&#xff08;指数移动平均&#xff09;计算快线和慢线的值&am…...

python线上学习进度报告

一、mooc学习 二、python123学习...

深入剖析ThreadLocal:原理、应用与最佳实践

深入剖析ThreadLocal&#xff1a;原理、应用与最佳实践 一、ThreadLocal的本质与价值 1.1 什么是ThreadLocal&#xff1f; ThreadLocal是Java提供的线程本地变量机制&#xff0c;允许每个线程拥有独立的变量副本&#xff0c;实现线程间的数据隔离。它通过“空间换时间”的方式…...

nginx 配置后端健康检查模块

nginx自带的针对后端节点健康检查的功能比较简单,通过默认自带的ngx_http_proxy_module 模块和ngx_http_upstream_module模块中的参数来完成,当后端节点出现故障时,自动切换到健康节点来提供访问。但是nginx不能事先知道后端节点状态是否健康,后端即使有不健康节点,负载均…...

路由交换实验

案例一&#xff1a;实施和配置RIPV2 1.给AR1配置接口 查看R1接口配置情况 2.配置三台路由的RIP协议&#xff0c;版本为version2 &#xff0c;关闭自动汇总&#xff0c;通告所有的直连接口 案例二&#xff1a;配置多区域的OSPF协议 1.配置R1的接口IP地址参数 2.配置r2,r3的接口参…...

主成分分析(PCA)是什么?简易理解版

文章目录 一、PCA的本质与核心价值二、数据中的"重要方向"&#xff1a;理解变异性三、主成分的数学基础四、荷载向量的深入理解五、PCA的计算过程详解5.1 数据预处理5.2 计算协方差矩阵5.3 特征分解5.4 主成分得分计算 六、PCA的实际应用解读七、PCA的工具与实现7.1 …...

Linux常用命令34——uname显示系统内核信息

在使用Linux或macOS日常开发中&#xff0c;熟悉一些基本的命令有助于提高工作效率&#xff0c;uname命令来自英文词组UNIX name的缩写&#xff0c;其功能是查看系统主机名、内核及硬件架构等信息。如果不加任何参数&#xff0c;默认仅显示系统内核名称&#xff08;相当于-s参数…...

Linux下使用openssh搭建sftp服务

创建 SFTP 用户组 为 SFTP 用户创建一个专用组&#xff1a; sudo groupadd sftpusers 创建 SFTP 用户 创建 SFTP 用户并将其添加到 sftpusers 组&#xff0c;同时指定用户的主目录和禁止 shell 访问&#xff1a; sudo useradd -g sftpusers -s /sbin/nologin username sud…...

C++ 复习(一)

命名空间 概念 &#xff1a; 命名空间的主要作用是创建一个新的作用域 里面可以放函数 变量 定义 为了防止命名冲突 实现 : 通过使用namespace 空间名 {} 在大括号中添加 内容 1. 这里命名空间允许嵌套 2. 在同一个工程中允许存在多个同名的命名空间 在最后编译…...

主备Smart Link + Monitor Link组网技术详细配置

1.实验拓扑 2.使用设备 eNSP模拟建议下行设备三台使用S3700模拟&#xff08;全部使用S5700可能会出现流量丢失等异常问题。&#xff09; 3.实验配置 [SW1]dis cu # sysname SW1 # vlan batch 100 110 # interface Ethernet0/0/1port link-type accessport default vlan 100 …...

【5G通信】redcap和bwp 随手记

在5G通信中&#xff0c;BWP&#xff08;Bandwidth Part&#xff09;是一种技术&#xff0c;允许终端设备在不同的带宽部分上进行通信&#xff0c;从而提高频谱效率和灵活性。BWP可以分为初始BWP&#xff08;Initial BWP&#xff09;、默认BWP&#xff08;Default BWP&#xff0…...

第三天 车联网云架构

一、车联网技术演进与行业变革 1.1 从传统Telematics到智能网联汽车 当我们驾驶着搭载智能网联系统的汽车时&#xff0c;车辆每秒会产生超过1GB的数据流量。这些数据包括&#xff1a; 高精度地图的实时更新ADAS传感器采集的环境信息车载娱乐系统交互数据车辆状态监控信息 传…...

手撕基于AMQP协议的简易消息队列-7(客户端模块的编写)

在MQClient中编写客户端模块代码 在MQClient中编写makefile文件来编译客户端模块 .PHONY:all all:PublichClient ConsumeClient PublichClient : PublichClient.cpp ../MQCommon/request.pb.cc ../MQCommon/message.pb.cc ../ThirdLib/lib/include/muduo/protobuf/codec.ccg …...

Spring Security(笔记)

第一步&#xff1a; 首先使用Intellij IDEA创建一个Spring Boot项目&#xff0c;JDK选择自己安装的1.8。点击Next后&#xff0c;编辑项目信息。然后跳转到选择依赖页面。 第二步&#xff1a; 添加Spring Security、Spring Web、Thymeleaf三个依赖。完成后等待项目构建完成。…...

通义灵码编码插件支持MCP

通义灵码MCP功能集成概述 通义灵码已深度集成魔搭社区&#xff08;ModelScope&#xff09;的MCP&#xff08;Model Context Protocol&#xff09;服务&#xff0c;为开发者提供了在IDE中直接调用AI能力的便捷通道。MCP作为标准化协议&#xff0c;通过定义Resources、Prompts和…...

问题 | 当前计算机视觉迫切解决的问题

当前计算机视觉领域虽然在技术上取得了显著进展&#xff0c;但仍面临一系列关键挑战。结合最新研究与应用现状&#xff0c;以下是最迫切需要解决的几大问题&#xff1a; 1. 数据质量与多样性不足 高质量标注数据的获取&#xff1a;训练高效模型依赖大量精准标注的数据&#x…...

C++ STL入门:vecto容器

C STL 系列入门&#xff1a;vector 动态数组 一、vector 容器核心特性 vector 是 C 标准库提供的动态数组容器&#xff0c;具有以下显著优势&#xff1a; 自动扩容机制&#xff1a;当插入元素超出当前容量时&#xff0c;自动申请新内存并迁移数据随机访问效率&#xff1a;支持…...

Java 线程全面概述

Java 线程全面概述 线程是程序执行的最小单元&#xff0c;是操作系统能够调度的最小单位。Java 提供了完善的线程支持&#xff0c;下面从基础概念到高级特性进行全面解析。 一、线程基础概念 1. 线程 vs 进程 特性进程线程资源占用独立内存空间共享进程内存切换成本高&#…...

高效文件夹迁移工具,轻松实现批量文件管理

软件介绍 DirMapper是一款专注于文件夹迁移的工具&#xff0c;可以快速完成文件的批量整理与位置调整。 功能特点 这款文件夹迁移工具提供两种操作模式&#xff1a;复制模式和移动模式&#xff0c;用户可以根据需求自行选择。如果需要保留原文件&#xff0c;可以选择复…...

sherpa:介绍

更多内容&#xff1a;XiaoJ的知识星球 目录 1. sherpa 介绍 1. sherpa 介绍 sherpa是 Next-gen Kaldi 项目的部署框架。 sherpa 支持在各种平台上部署与语音相关的预训练模型&#xff0c;并提供多种语言绑定。 目前&#xff0c;sherpa 拥有以下子项目&#xff1a; k2-fsa/sh…...

Android Studio Gradle 中 只显示 Tasks 中没有 build 选项解决办法

一、问题描述 想把项目中某一个模块的代码单独打包成 aar ,之前是点击 AndroidStudio 右侧的 Gradle 选项&#xff0c;然后再点击需要打包的模块找到 build 进行打包&#xff0c;但是却发现没有 build 选项。 二、解决办法 1、设置中勾选 Configure all Gradle tasks… 选项 …...

手撕基于AMQP协议的简易消息队列-6(服务端模块的编写)

在MQServer中编写服务端模块代码 在MQServer中编写makefile文件来编译服务端模块 .PHONY: server CFLAG -I../ThirdLib/lib/include LFLAG -L../ThirdLib/lib/lib -lgtest -lprotobuf -lsqlite3 -pthread -lmuduo_net -lmuduo_base -lz server:server.cpp ../MQCommon/messag…...

面试实践AND面经热点题目总结

1、对于Rocketmq消息积压、丢失如何解决&#xff1f; 消息积压原因以及解决方案 &#x1f3af; 产生原因&#xff1a; 消费者处理能力弱&#xff0c;消费速度远低于生产速度&#xff1b; 网络不稳定&#xff0c;消费者拉取消息失败&#xff1b; 消费端异常&#xff08;如处理…...

MySQL基础关键_012_事务

目 录 一、概述 二、ACID 四大特性 三、MySQL 事务 四、事务隔离级别 1.说明 2.现象 &#xff08;1&#xff09;脏读 &#xff08;2&#xff09;不可重复读 &#xff08;3&#xff09;幻读 3.查看隔离级别 4.设置隔离级别 5.隔离级别 &#xff08;1&#xff09;初始…...

Missashe考研日记-day35

Missashe考研日记-day35 1 专业课408 学习时间&#xff1a;3h学习内容&#xff1a; 完结撒花&#xff01;&#xff01;今天把OS最后一节的内容学完了&#xff0c;操作系统也算是告一段落了&#xff0c;接下来是计网时间&#xff01;不过计网我是上学期才学过的&#xff0c;当…...

如何添加二级域名

在 华为云 上添加二级域名&#xff08;如 sub.example.com&#xff09;主要涉及 DNS解析配置 和 服务器绑定 两个步骤。以下是详细操作指南&#xff1a; 一、前提条件 已拥有 主域名&#xff08;如 example.com&#xff09;并完成 ICP备案&#xff08;若服务器在中国大陆&#…...

【数据结构】01Trie

什么是 01Trie? 01Trie是字典树的一种变种&#xff0c;其只有两种情况&#xff0c;即 0 和 1&#xff0c;实现方式其实和字典树是一样的 有什么用呢&#xff1f; 其一般用于解决异或问题&#xff0c;是一种快速的数据结构&#xff0c;某些情况下可以无脑套用 实现方式&#…...

使用 CDN 在国内加载本地 PDF 文件并处理批注:PDF.js 5.x 实战指南

PDF.js 是一个强大的开源 JavaScript 库&#xff0c;用于在 Web 浏览器中渲染 PDF 文件。它由 Mozilla 开发&#xff0c;能够将 PDF 文档绘制到 HTML5 Canvas 或 SVG 上&#xff0c;无需任何本机代码或浏览器插件。对于许多需要在网页中展示 PDF 内容的应用场景来说&#xff0c…...

SpringBoot指定项目层日志记录

1、新建一个Springboot项目&#xff0c;添加Lombok依赖&#xff08;注意&#xff1a;这里使用的Lombok下的Slf4j快速日志记录方式&#xff09; <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependenc…...

使用Mathematica内置函数绘制Sierpinski地毯

除了SierpinskiCurve之外&#xff0c;Mathematica还内置了SierpinskiMesh这个函数&#xff0c;用来绘制地毯。 SierpinskiMesh[n] gives a mesh region representing the n-step Sierpiński triangle. SierpinskiMesh[n,d] gives the n-step Sierpiński sponge in dimension…...

CMake笔记(简易教程)

CMake笔记 概述&#xff08;需要提前了解的知识&#xff09; 一个c/c程序从代码到生成二进制文件&#xff0c;需要经历的几个关键步骤&#xff1a;预编译&#xff08;预处理&#xff09;、编译、汇编、连接 【编译链接的几个步骤】 编译器&#xff1a;目前市面常见的编译器有…...

现代健康养生新范式:多维度守护身心活力

在快节奏的现代生活中&#xff0c;健康养生是维持高品质生活的关键。从环境调节到生活习惯养成&#xff0c;多个维度的协同发力&#xff0c;才能为健康注入持久动力。​ 良好的生活环境是健康的基础。室内空气流通至关重要&#xff0c;每天开窗通风 2-3 次&#xff0c;每次 30…...

推测式思维树:让大模型快速完成复杂推理

论文标题 Accelerating Large Language Model Reasoning via Speculative Search 论文地址 https://www.arxiv.org/pdf/2505.02865 作者背景 中科大&#xff0c;华为诺亚方舟实验室&#xff0c;天津大学 ICML 2025接收 动机 之前介绍过多篇投机解码&#xff08;推测式解…...

软考错题(三)

telnet协议是一种基于TCP的远程登录协议 占用辅助空间最多的是归并排序 直接插入&#xff0c;堆排&#xff0c;简单选择&#xff0c;冒泡的空间复杂度是O(1) 快排是O(logn) 归并是O(n) B树的叶子节点通过指针链接为有序表&#xff0c;不是b-树 python中切片语法[start,end,s…...

注解的定义

一、理论说明 1. 注解的定义 Java 注解是从 JDK 5.0 开始引入的一种元数据机制&#xff0c;它可以为代码添加额外的信息&#xff0c;这些信息不影响程序的运行逻辑&#xff0c;但可以在编译期、类加载期或运行期被读取和处理。注解本质上是一种特殊的接口&#xff0c;所有注解…...

企业微信自建消息推送应用

企业微信自建应用来推送消息 前言 最近有个给特定部门推送消息的需求&#xff0c;所以配置一个应用专门用来推送消息。实现过程大致为&#xff1a;服务器生成每天的报告&#xff0c;通过调用API来发送消息。以前一直都是发邮件&#xff0c;整个邮箱里全是报告文件&#xff0c…...

swagger3融入springboot

标签&#xff1a; 放controller上面 Api(description "xxx") 放方法上面 Operation(summary "xxx") 引入&#xff1a; 我用的是swagger3.X 需要在yml配置文件中加上&#xff1a; spring:mvc:pathmatch:matching-strategy: ant_path_matcher 然后生…...

CH32V208GBU6沁恒绑定配对获取静态地址

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…...

[计算机科学#11]:编程语言简史,从二进制到简约表达的华丽转身,造就原因——“懒”

【核知坊】&#xff1a;释放青春想象&#xff0c;码动全新视野。 我们希望使用精简的信息传达知识的骨架&#xff0c;启发创造者开启创造之路&#xff01;&#xff01;&#xff01; 内容摘要&#xff1a; 由于早期的编程需要直接操作硬件&#xff0c;例如使…...

Kubernetes HPA 深度解析:生产环境自动扩缩容实战指南

一、HPA 核心原理剖析 1. 运作机制三步曲 (图示&#xff1a;指标采集 → 决策计算 → 执行扩缩容的完整闭环) 指标采集层&#xff1a;通过 Metrics Server/Prometheus 等组件实时收集 CPU、内存或自定义指标决策计算层&#xff1a;根据当前指标值与目标阈值的比例计算所需副本…...

Matlab 四分之一车体被动和模糊控制对比

1、内容简介 Matlab215-四分之一车体被动和模糊控制对比 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略...

pm2如何执行脚本批量启动多个服务

在 PM2 中批量启动多个服务&#xff0c;可以通过以下几种高效方式实现&#xff0c;具体操作如下&#xff1a; 方法1&#xff1a;使用 ecosystem.config.js 配置文件&#xff08;推荐&#xff09; 步骤1&#xff1a;生成配置文件 在项目根目录运行以下命令&#xff0c;生成模板…...

Debian系统详解

以下是关于 Debian 操作系统 的超详细深度解析&#xff0c;涵盖历史、架构、功能特性、管理细节及应用场景等方面&#xff0c;帮助你全面掌握这一经典 Linux 发行版&#xff1a; 一、Debian 概述&#xff1a;开源社区的基石 1. 历史与定位 • 诞生&#xff1a;1993 年由 Ian…...

Dify X 奇墨科技,让AI大模型从“巨头专属”变为“触手可及”

AI大模型和AI Agent蓬勃发展&#xff0c;企业比拼的已不仅是AI技术储备&#xff0c;更是AI应用落地的实战能力。奇墨科技正式成为 AI 应用开发平台Dify中国大陆区企业版合作伙伴&#xff0c;帮助企业更便捷地接触到Dify并使用其开发AI应用。 Dify 是一款简单易用的 LLM 应用开…...

CSS相对定位与绝对定位

在网页设计里&#xff0c;相对定位&#xff08;Relative Positioning&#xff09;和绝对定位&#xff08;Absolute Positioning&#xff09;是 CSS&#xff08;层叠样式表&#xff09;里控制元素位置的关键手段。下面为你详细讲解它们的概念、特点与应用场景。 相对定位 概念…...

正则表达式(Regular Expression)详解

正则表达式&#xff08;简称"regex"或"regexp"&#xff09;是一种强大的文本模式匹配工具&#xff0c;它使用特定语法来描述、匹配和操作字符串。 基本概念 正则表达式是由普通字符&#xff08;如字母a到z&#xff09;和特殊字符&#xff08;称为"元…...

OpenCV-Python (官方)中文教程(部分一)_Day22

22.3 2D直方图 在前面的部分我们介绍了如何绘制一维直方图,之所以称为一维,是因为我们只考虑了图像的一个特征&#xff1a;灰度值。但是在 2D 直方图中我们就要考虑 两个图像特征。对于彩色图像的直方图通常情况下我们需要考虑每个的颜色&#xff08;Hue&#xff09;和饱和度&…...