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

【Java多线程】1——多线程知识回顾

1 多线程知识回顾

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
笔记仓库👉https://github.com/A-BigTree/tree-learning-notes
个人主页👉https://www.abigtree.top
⭐⭐⭐⭐⭐⭐


如果可以,麻烦各位看官顺手点个star~😊

如果文章对你有所帮助,可以点赞👍收藏⭐支持一下博主~😆


文章目录

  • 1 多线程知识回顾
    • 1.1 基础概念
      • 1.1.1 程序、进程、线程
        • 程序
        • 进程
        • 线程
      • 1.1.2 串行、并行、并发
        • 串行
        • 并行和并发
      • 1.1.3 sleep()和wait()
        • `sleep()`进入等待状态不释放锁
        • `wait()`进入等待状态释放锁
        • 小结
      • 1.1.4 同步方法和同步代码块
        • 相同点
        • 区别
        • 小结
    • 1.2 创建多线程
      • 1.2.1 继承Thread类
        • 实现方法
        • `start()` 方法和 `run()` 方法区别
        • 评价
      • 1.2.2 实现Runnable接口
        • 实现接口形式
        • 匿名内部类形式
        • Lambda表达式
      • 1.2.3 使用Callable接口配合FutureTask
        • `FutureTask`类和`Runnable`接口的关系
        • Future接口
        • `FutureTask`类的构造器
        • Callable接口
        • 测试代码
        • callable和Runnable对比
      • 1.2.4 线程池
        • 参考代码
      • 1.2.5 并行计算
      • 1.2.6 Timer定时任务
      • 1.2.7 Spring异步方法
        • 准备SpringBoot环境
        • 使用异步方法
    • 1.3 线程状态与生命周期
      • 1.3.1 线程状态枚举类
        • 源代码
        • 说明
      • 1.3.2 线程的生命周期
    • 1.4 线程间通信
      • 1.4.1 核心语法
        • `Object` 类的 `wait()` 方法
        • `Object` 类的 `notify()` 方法
        • `Object` 类的 `notifyAll()` 方法
      • 1.4.2 虚假唤醒

1.1 基础概念

1.1.1 程序、进程、线程

程序
  • 程序从开发到发布的过程:源程序(源代码) → 打包封装 → 应用软件 ;

  • 笼统的来说,源程序、应用软件都可以称之为『程序』;

  • 相对于进程、线程来说,程序是一个静态的概念;

进程
  • 内部视角:程序运行起来就是一个进程。所以相对于程序来说,进程是一个动态的概念;
  • 外部视角:站在操作系统的层次上来说,现代的大型操作系统都是支持多进程模式运行的,这样操作系统就可以同时执行很多个任务;
线程

在一个进程中,需要同时处理多个不同任务,每一个任务由一个线程来执行。从这个意义上来说,可以把进程看做是线程的容器。

在这里插入图片描述

1.1.2 串行、并行、并发

串行

多个操作在同一个线程内按顺序执行。这种情况下的工作模式我们往往也称之为:同步。按照同步模式执行的多个操作,当前操作没有结束时,下一个操作就必须等待。处于等待中的状态往往也称为:阻塞(block)。

并行和并发

并行和并发都是以异步的模式来执行操作的。异步工作模式下不同线程内的操作互相不需要等待。

  • 并行:多个 CPU(或 CPU 核心)同时执行多个任务
  • 并发:一个 CPU(或 CPU 核心)同时执行多个任务

1.1.3 sleep()和wait()

二者最关键的区别是下面两点:

  • sleep() 会让线程拿着锁去睡;
  • wait() 会让线程放开锁去睡;
sleep()进入等待状态不释放锁
// 1、创建一个对象,作为锁对象
Object lockObj = new Object();// 2、创建执行 sleep 的线程
new Thread(()->{System.out.println(Thread.currentThread().getName() + " begin");// ※ 两个线程使用同一个锁对象,就会存在竞争关系synchronized (lockObj) {System.out.println(Thread.currentThread().getName() + " get lock");try {// ※ sleep() 方法拿着锁去睡TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " release lock");}System.out.println(Thread.currentThread().getName() + " end");}, "thread-a").start();// ※ 让主线程睡一会儿,确保 a 线程先启动
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}// 3、创建竞争锁的线程
new Thread(()->{System.out.println(Thread.currentThread().getName() + " begin");// ※ 两个线程使用同一个锁对象,就会存在竞争关系synchronized (lockObj) {System.out.println(Thread.currentThread().getName() + " get lock");}System.out.println(Thread.currentThread().getName() + " end");
}, "thread-b").start();

打印结果:

thread-a begin

thread-a get lock

thread-b begin

thread-a release lock

thread-b get lock

thread-b end

thread-a end

wait()进入等待状态释放锁
// 1、创建一个对象,作为锁对象
Object lockObj = new Object();// 2、创建执行 sleep 的线程
new Thread(()->{System.out.println(Thread.currentThread().getName() + " begin");// ※ 两个线程使用同一个锁对象,就会存在竞争关系synchronized (lockObj) {System.out.println(Thread.currentThread().getName() + " get lock");try {// ※ wait() 方法放开锁去睡lockObj.wait(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " release lock");}System.out.println(Thread.currentThread().getName() + " end");}, "thread-a").start();// ※ 让主线程睡一会儿,确保 a 线程先启动
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {}// 3、创建竞争锁的线程
new Thread(()->{System.out.println(Thread.currentThread().getName() + " begin");// ※ 两个线程使用同一个锁对象,就会存在竞争关系synchronized (lockObj) {System.out.println(Thread.currentThread().getName() + " get lock");}System.out.println(Thread.currentThread().getName() + " end");
}, "thread-b").start();

打印结果:

thread-a begin

thread-a get lock

thread-b begin

thread-b get lock

thread-b end

thread-a release lock

thread-a end

小结
wait()sleep()
声明位置Object 类Thread 类
影响线程的方式通过调用 wait() 方法的对象影响到线程直接影响当前线程
性质非静态方法静态方法
释放锁资源放开锁进入等待不释放锁进入等待
同步要求必须在同步上下文中使用不要求在同步上下文中
应用场景用于线程间通信用来让线程暂停一段时间

1.1.4 同步方法和同步代码块

相同点

都会用到synchronized关键字

区别
锁对象锁定范围
同步代码块由程序员指定代码块的范围(灵活)
同步方法静态:类.class
非静态:this
整个方法体
小结
  • 结论1:静态同步方法使用类.class作为锁对象;非静态同步方法使用this作为锁对象;
  • 结论2:多个线程如果使用同一个锁对象就会有竞争关系;否则没有竞争关系;

1.2 创建多线程

无论有多少种形式,创建多线程的真正的方法,其实只有两种:

继承 Thread 类

实现 Runnable 接口

其它形式都是这两种方式的变体

1.2.1 继承Thread类

实现方法
  • 第一步:继承 Thread 类;
  • 第二步:重写 run() 方法;
  • 第三步:创建 Thread 子类对象;
  • 第四步:调用 start() 方法启动线程;
public class CreateThread01Extends {public static void main(String[] args) {DemoThread demo = new DemoThread("AAA");demo.start();}}class DemoThread extends Thread {public DemoThread(String threadName) {super(threadName);}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " thread working ...");}
}
start() 方法和 run() 方法区别

调用 run() 方法仅仅只是调用了一个子类中重写的父类方法,并没有真正开启一个新的线程,还是在当前线程运行,也就是 main 线程。

评价

因为 Java 是单继承的,一个类继承了 Thread 类就不能继承其它类,所以通常不采用这个办法创建多线程。

1.2.2 实现Runnable接口

实现接口形式
public class CreateThread02Impl {public static void main(String[] args) {// 第四步:创建实现了 Runnable 接口的类的对象MyRunnableThread runnable = new MyRunnableThread();// 第五步:创建 Thread 类对象// 参数1:runnable 对象// 参数2:线程名称Thread thread = new Thread(runnable, "thread 002");// 第六步:调用 Thread 对象的 start() 方法启动线程thread.start();}}// 第一步:实现 Runnable 接口
class MyRunnableThread implements Runnable {// 第二步:实现 run() 方法@Overridepublic void run() {// 第三步:编写线程中的逻辑代码System.out.println(Thread.currentThread().getName() + " is working");}
}
匿名内部类形式
// 第一步:以匿名内部类的方式创建 Runnable 接口类型的对象
Runnable runnable = new Runnable() {@Overridepublic void run() {// 第二步:编写线程中的逻辑代码System.out.println(Thread.currentThread().getName() + " is working");}
};// 第三步:创建 Thread 类对象
// 参数1:runnable 对象
// 参数2:线程名称
Thread thread = new Thread(runnable, "thread 003");// 第四步:调用 Thread 对象的 start() 方法启动线程
thread.start();
Lambda表达式

声明变量:

// 编写 Lambda 表达式的口诀:
// 复制小括号
// 写死右箭头
// 落地大括号// 第一步:以匿名内部类的方式创建 Runnable 接口类型的对象
Runnable runnable = () -> {// 第二步:编写线程中的逻辑代码System.out.println(Thread.currentThread().getName() + " is working");
};// 第三步:创建 Thread 类对象
// 参数1:runnable 对象
// 参数2:线程名称
Thread thread = new Thread(runnable, "thread 004");// 第四步:调用 Thread 对象的 start() 方法启动线程
thread.start();

不声明变量:

// 第一步:创建 Thread 类对象并调用 start() 方法启动线程
// 参数1:以Lambda 表达式形式创建的 runnable 对象
// 参数2:线程名称
new Thread(() -> {// 第二步:编写线程中的逻辑代码System.out.println(Thread.currentThread().getName() + " is working");
}, "thread 005").start();

1.2.3 使用Callable接口配合FutureTask

该方案最核心的价值是:使用 Callable 接口限定的功能 + Future 接口限定的功能 = 汇总各个线程执行结果 最终执行汇总操作的这一步会被阻塞,直到前面各个线程完成了计算。

FutureTask类和Runnable接口的关系

在这里插入图片描述

从继承关系能够看到,FutureTask本身也间接实现了Runnable接口。FutureTask类的对象也是Runnable接口的实例,可以用于在创建Thread对象时,传入Thread构造器。

Future接口

停止任务:

boolean cancel(boolean mayInterruptIfRunning);

如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true 时才会中断任务。

获取任务的结果:

V get() throws InterruptedException, ExecutionException;

如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。

判断任务是否完成:

boolean isDone();

如果任务完成,则返回true,否则返回false。

FutureTask类的构造器

介绍:

FutureTask 类兼具 RunnableFuture 接口的功能,并方便地将两种功能组合在一起。关于 FutureTask 类的使用有如下建议:

  • 在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成;
  • 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态;
  • 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果;
  • 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get() 方法;
  • 一旦计算完成,就不能再重新开始或取消计算;
  • get() 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常;
  • get() 只执行一次,因此get() 方法放到最后;

可以使用的构造器:

    public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;       // ensure visibility of callable}

根据这个构造器,我们知道,创建 FutureTask 对象时,传入一个 Callable 类型的对象即可。

Callable接口
@FunctionalInterface
public interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

从 call() 方法的声明我们可以看出,它有一个返回值。这个返回值可以将当前线程内计算结果返回。

测试代码
// 1.创建三个FutureTask对象,封装三个线程的执行逻辑
FutureTask<Integer> task01 = new FutureTask<>(() -> {int result = (int) (Math.random() * Math.random() * 100);System.out.println(Thread.currentThread().getName());return result;
});
FutureTask<Integer> task02 = new FutureTask<>(() -> {int result = (int) (Math.random() * Math.random() * 1000);System.out.println(Thread.currentThread().getName());return result;
});
FutureTask<Integer> task03 = new FutureTask<>(() -> {int result = (int) (Math.random() * Math.random() * 10000);System.out.println(Thread.currentThread().getName());return result;
});// 2.创建三个线程对象,然后启动线程
new Thread(task01, "thread01").start();
new Thread(task02, "thread02").start();
new Thread(task03, "thread03").start();// 3.上面三个线程执行完成后,可以收集它们各自运算的结果
Integer task01Result = task01.get();
Integer task02Result = task02.get();
Integer task03Result = task03.get();System.out.println("task01Result = " + task01Result);
System.out.println("task02Result = " + task02Result);
System.out.println("task03Result = " + task03Result);
callable和Runnable对比
Runnable接口Callable接口
重写run()方法重写call()方法
run()没有返回值call()有返回值
run()没有声明抛出异常call()声明抛出Exception
没有汇总各个线程结果的机制有汇总各个线程结果的机制

1.2.4 线程池

参考代码
// 1.创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(5);// 2.给线程池对象分配任务,每一个任务是一个线程
pool.execute(() -> {System.out.println(Thread.currentThread().getName() + " " + new Date());
});pool.execute(() -> {System.out.println(Thread.currentThread().getName() + " " + new Date());
});pool.execute(() -> {System.out.println(Thread.currentThread().getName() + " " + new Date());
});

1.2.5 并行计算

List<String> list = Arrays.asList("a", "b", "c", "d", "e");// 串行计算
list.stream().forEach(System.out::print);System.out.println();// 并行计算
list.parallelStream().forEach(System.out::print);

1.2.6 Timer定时任务

// 1、创建 Timer 对象封装定时任务中要执行的操作
// 每一个 Timer 对象会使用一个线程来执行定时任务
Timer timer01 = new Timer();// 2、调用 schedule() 指定任务和执行周期
// 参数1:timerTask 封装具体任务操作
// 参数2:delay 指定定时任务延迟多久后开始执行
// 参数3:period 指定定时任务执行的时间间隔
timer01.schedule(new TimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() +" is working");}
}, 0, 1000);Timer timer02 = new Timer();timer02.schedule(new TimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() +" is working");}
}, 0, 1000);

1.2.7 Spring异步方法

在 Spring 环境下,如果组件 A(假设是 ControllerA)要调用组件 B(假设是 ServiceB)的多个方法,而且希望这些方法能够异步执行。

准备SpringBoot环境

引入依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.5.2</version></dependency>
</dependencies>

创建主启动类:

@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}

创建Service:

@Service
public class DemoService {public void doSth() {System.out.println("Demo Service " + Thread.currentThread().getName());}}

创建Controller:

@RestController
public class DemoController {@Autowiredprivate DemoService demoService;@RequestMapping("/demo/test/async")public String callServiceMethod() {demoService.doSth();demoService.doSth();demoService.doSth();demoService.doSth();demoService.doSth();return "success";}}
使用异步方法

开启异步功能:

在主启动类使用 @EnableAsync 注解:

// 开启支持异步方法调用功能
@EnableAsync
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}

标记异步方法:

在想要异步调用的方法上使用 @Async 注解:

@Service
public class DemoService {// 在想要实现异步调用的方法上加 @Async注解@Asyncpublic void doSth() {System.out.println("Demo Service " + Thread.currentThread().getName());}}

1.3 线程状态与生命周期

1.3.1 线程状态枚举类

源代码
    public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread.  A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>*   <li>{@link Object#wait() Object.wait} with no timeout</li>*   <li>{@link #join() Thread.join} with no timeout</li>*   <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called <tt>Object.wait()</tt>* on an object is waiting for another thread to call* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on* that object. A thread that has called <tt>Thread.join()</tt>* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>*   <li>{@link #sleep Thread.sleep}</li>*   <li>{@link Object#wait(long) Object.wait} with timeout</li>*   <li>{@link #join(long) Thread.join} with timeout</li>*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;}
说明
英文名称中文名称含义
NEW新建线程对象刚创建
RUNNABLE就绪等待 CPU 时间片
RUNNING运行得到了 CPU 时间片,正在执行
BLOCKED阻塞等待同步锁
WAITING等待等待被唤醒
TIMED_WAITING限时等待在进入等待状态时设定了等待时间。时间一到自动回到就绪状态
TERMINATED终止线程因为代码执行完成或抛异常而停止执行

1.3.2 线程的生命周期

在这里插入图片描述

1.4 线程间通信

在多线程模式下进行工作,除了要考虑各个线程之间是否同步、如何竞争锁等问题,还要考虑这样一个问题:线程之间有的时候需要相互配合来共同完成一件事情。 把一个大的任务拆分成多个不同的任务线,每个任务线中都有更小的执行步骤。各个线程之间需要彼此配合:A 线程执行一步唤醒 B 线程,自己等待;B 线程执行一步,唤醒 A 线程,自己等待……

1.4.1 核心语法

Object 类的 wait() 方法

Causes the current thread to wait until another thread invokes the java.lang.Object#notify() method or the java.lang.Object#notifyAll() method for this object.

  • wait() 方法会导致当前线程进入等待状态;
  • 必须另外一个线程调用 notify()notifyAll() 方法来唤醒
  • “for this object” 表示还是要使用 同一个对象 分别调用 wait()notify()notifyAll() 这些方法;
Object 类的 notify() 方法

Wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is chosen to be awakened.

  • notify() 方法只唤醒一个线程;
  • 处于等待状态的线程会被存放在对象监视器中的一个数组中;
  • 如果在这个对象的监视器中维护的处于等待状态的线程是多个,那么 notify() 方法会随机唤醒一个;
  • notfiy() 方法无法精确唤醒一个指定的线程,这个需求可以通过 Lock + Condition 方式实现(定制化通信);
Object 类的 notifyAll() 方法

Wakes up all threads that are waiting on this object’s monitor.

唤醒当前对象监视器上等待的**所有**线程。

1.4.2 虚假唤醒

在这里插入图片描述

这种情况,我们称之为:虚假唤醒。

要解决虚假唤醒问题,就需要对线程间通信时的判断条件使用 while 循环结构来执行,而不是 if 分支判断。

在这里插入图片描述

相关文章:

【Java多线程】1——多线程知识回顾

1 多线程知识回顾 ⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记仓库&#x1f449;https://github.com/A-BigTree/tree-learning-notes 个人主页&#x1f449;https://www.abigtree.top ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点个star…...

音视频处理 - 音频概念详解,码率,采样率,位深度,声道,编码

1. 音频采样 与视频不同&#xff0c;音频的最小单位不是一帧&#xff0c;而是一个采样。 采样是当前一刻声音的声音样本&#xff0c;样本需要经过数字转换才能存储为样本数据。 真实声音是连续的&#xff0c;但是在计算机中&#xff0c;声音是离散且均匀的声音样本。 2. 位深…...

【PLC】PROFIBUS(二):总线协议DP、PA、FMS

1、总线访问协议 (FDL) 1.1、多主通信 多个主设备间&#xff0c;使用逻辑令牌环依次向从设备发送命令。 特征&#xff1a; 主站间使用逻辑令牌环、主从站间使用主从协议主站在一个限定时间内 (Token Hold Time) 对总线有控制权从站只是响应一个主站的请求它们对总线没有控制…...

Mysql配置autocommit实际使用(慎用)

以下内容都是基于MySQL5.7。所有操作建议在MySQL客户端执行。navicat可能会先意想不到的问题 在导入频繁执行update、insert的时候&#xff0c;可以考虑关闭MySQL的自动提交 首先查询当前的状态 1开启 0关闭 select autocommit;设置本次连接关闭自动提交(如果需要永久关闭请修…...

Mac电脑高清媒体播放器:Movist Pro for mac下载

Movist Pro for mac是一款专为Mac操作系统设计的高清媒体播放器&#xff0c;支持多种常见的媒体格式&#xff0c;包括MKV、AVI、MP4等&#xff0c;能够流畅播放高清视频和音频文件。Movist Pro具有强大的解码能力和优化的渲染引擎&#xff0c;让您享受到更清晰、更流畅的观影体…...

Linux 网站定时备份+滚动删除脚本:文件、数据库(命令篇)

为确保数据安全&#xff0c;我们定期对网站相关文件和数据进行备份&#xff0c;以防止因各种原因导致的丢失情况。同时&#xff0c;考虑到服务器空间的限制&#xff0c;我们也会定期清理历史备份数据。 本文以 CentOS 7.9 系统为例&#xff0c;记录如何通过脚本和定时任务实现备…...

Cache缓存:HTTP缓存策略解析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

智慧公厕的全域感知、全网协同、全业务融合和全场景智慧赋能

公共厕所是城市的重要组成部分&#xff0c;为市民提供基本的生活服务。然而&#xff0c;传统的公厕管理模式存在诸多问题&#xff0c;如排队等候时间长、卫生状况差、空气质量差等&#xff0c;严重影响市民的出行和生活质量。为了解决这些问题&#xff0c;智慧公厕应运而生&…...

Day44:WEB攻防-PHP应用SQL盲注布尔回显延时判断报错处理增删改查方式

目录 PHP-MYSQL-SQL操作-增删改查 PHP-MYSQL-注入函数-布尔&报错&延迟 基于布尔的SQL盲注-逻辑判断(需要有回显,没回显搞不了)跟union需要的条件差不多 基于时间的SQL盲注-延时判断(不需要任何回显) 基于报错的SQL盲注-报错回显(需要报错回显&#xff0c;没报错回…...

C# 将 Word 转文本存储到数据库并进行管理

目录 功能需求 范例运行环境 设计数据表 关键代码 组件库引入 Word文件内容转文本 上传及保存举例 得到文件Byte[]数据方法 查询并下载Word文件 总结 功能需求 将 WORD 文件的二进制信息存储到数据库里&#xff0c;即方便了统一管理文件&#xff0c;又可以实行权限控…...

VRRP协议

目录 VRRP协议基本概述 VRRP的基本结构 设备类型 VRRP工作原理 VRRP配置的实现 VRRP的实验 VRRP协议基本概述 1.VRRP能够在不改变组网的情况下&#xff0c;将多台路由器虚拟成一个虚拟路由器&#xff0c;通过配置虚拟路由器 的IP地址为默认网关&#xff0c;实现网关的备…...

Python学习之-基础语法

第1关&#xff1a;行与缩进 任务描述 本关任务&#xff1a;改正代码中不正确的缩进&#xff0c;使其能够正常编译&#xff0c;并输出正确的结果。 编程要求 根据提示&#xff0c;改正右侧编辑器中代码的缩进错误&#xff0c;使其能够正确运行&#xff0c;并输出结果。 测试说明…...

Java八股文(SpringCloud Alibaba)

Java八股文のSpringCloud Alibaba SpringCloud Alibaba SpringCloud Alibaba Spring Cloud Alibaba与Spring Cloud有什么区别&#xff1f; Spring Cloud Alibaba是Spring Cloud的衍生版本&#xff0c;它是由Alibaba开发和维护的&#xff0c;相比于Spring Cloud&#xff0c;它在…...

【物联网开源平台】tingsboard安装与编译

别看这篇了&#xff0c;这篇就当我的一个记录&#xff0c;我有空我再写过一篇&#xff0c;编译的时候出现了一个错误&#xff0c;然后我针对那一个错误执行了一个命令&#xff0c;出现了绿色的succes,我就以为整个tingsboard项目编译成功了&#xff0c;后面发现的时候&#xff…...

俚语加密漫谈

俚语加密是一种古老而有效的通信方式&#xff0c;将特定词语或短语在群体内赋予特殊含义&#xff0c;从而隐藏真实信息。类似于方言&#xff0c;它在历史上的应用不可忽视。随着计算机时代的到来&#xff0c;现代密码学通过数学运算编织密语&#xff0c;使得加密变得更加高深莫…...

【Java程序设计】【C00368】基于(JavaWeb)Springboot的箱包存储系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…...

Mysql中的执行计划怎么分析?

一、背景 在我们日常工作中&#xff0c;我们可能会遇到一些慢SQL语句或者要对一些SQL进行性能优化&#xff0c;那么就需要使用explain对SQL进行执行计划分析了。Mysql中的执行计划可以通过EXPLAIN或DESCRIBE关键字获取&#xff0c;当我们拿到执行计划后可以帮助我们分析这条sq…...

sever00启动AList

sever00启动AList cd ~/domains/alist && ~/.npm-global/bin/pm2 start ./alist -- server 其他 Serv00是一个提供免费的Virtual Host的平台&#xff0c;其托管平台使用的是FreeBSD系统&#xff0c;并不是Linux。每个账号有效期10年&#xff0c;超过三个月不登入Pan…...

【产品经理】进阶为一名优秀的数字孪生与仿真产品经理

数字孪生和仿真这个领域的内容太前沿了&#xff0c;很多经验、心得都没有对外流传。对于想成为这种产品经理的同学来说比较困难。 数字孪生&#xff1a;百度的解释是&#xff0c;数字孪生是充分利用物理模型、传感器更新、运行历史等数据&#xff0c;集成多学科、多物理量、多尺…...

CloudCompare 二次开发(29)——最小二乘拟合平面

目录 一、概述二、代码集成三、结果展示一、概述 使用CloudCompare实现的最小二乘拟合平面。具体计算原理见:PCL 最小二乘拟合平面。 二、代码集成 1、mainwindow.h文件public中添加: void doActionPCLLeastSquareFitPlane(); // 最小二乘拟合平面2、mainwindow.cpp文件…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...