编程必备:JAVA多线程详解

目录

前言

1.入门多线程

1.1. 线程、进程、多线程、线程池

1.2.并发、串行、并行

1.3. 线程的实现方式

1.3.1. 继承 Thread 类

1.3.2. 实现 Runnable 接口

1.3.3. 使用 Callable 和 Future

1.3.4. 使用线程池

1.4.线程的状态

1.5. 线程常用方法

1.5.1 sleep()

1.4.2 join()

1.5.3 yield()

1.5.4.wait() 和 notify() 

1.5.5.-interrupt()方法和stop()方法 

1.5.6. setPriority (int newPriority)、getPriority()

1.6.线程调度

2.进阶多线程

2.1.多线程引发问题

2.2.多线程解决方法

2.2.1. 锁机制

2.2.3. 线程池

2.2.4. 并发工具类

3.面试题

3.1.sleep()和wait()有什么区别?

3.2.如何停止一个处在运行态的线程

3.3.interrupt()、interrupted()、isInterrupted()方法的区别

3.4.系统创建的线程过多会有什么影响

4.写在最后

鸣谢


前言

多线程已经成为一种常见的编程模式,广泛应用于各种不同类型的应用程序中。

本篇博客文章中,我们将会探讨多线程编程的相关知识和技巧。通过代码示例和实际应用案例来深入了解多线程的具体实现和应用方法,帮助更好地掌握多线程编程技术,提高程序效率和性能。后期随学习深入还会补充修改。

1.入门多线程

1.1. 线程、进程、多线程、线程池

线程

在计算机科学中,线程是指进程中的一个单独的执行路径。一个进程可以包含多个线程,每个线程都可以并行执行不同的任务。多线程编程是指在同一时间内运行多个线程来完成多个任务。

多线程

多线程是指在同一时间内运行多个线程来完成多个任务。多线程提高程序的性能和响应速度。但是增加了代码的复杂性,同时需要考虑线程安全和死锁等问题。

线程池

线程池是一组预先创建的线程,它们可以被重复使用来执行多个任务。使用线程池可以避免在创建和销毁线程时产生额外的开销,从而提高程序的性能。Java 中提供了 Executor 框架来实现线程池。

进程

进程即一段程序的执行过程,是计算机中的程序关于某数据集合上的一次运行活动,是系统分配资源的最小单位

线程与进程的区别

  • 根本区别:进程是操作系统资源分配的最小单位,而线程是处理任务调度和执行的最小单位
  • 资源开销:每个进程都有单独的代码和数据空间,进程之间的切换会有较大的开销;线程可以开做轻量级的进程,同一类线程共享代码和数据空间,每个线程有自己独立的运行栈和程序计数器,线程之间的切换开销较小
  • 包含关系:一个进程可以包含多个线程,这些线程可以共享同步资源
  • 内存分配:同一个进程中的所有线程共享本进程的地址空间和资源,而进程之间的地址空间和资源相互独立
  • 影响关系:一个进程崩溃后,不会影响其他进程;而一个线程崩溃后其他线程也会收到影响,从而整个进程崩溃
  • 执行过程:每个独立的进程都有程序运行的入口,顺序执行序列和出口,但线程不能独立执行,必须依存于进程

1.2.并发、串行、并行

并发、串行、并行概念

  • 并发:指多个任务在同一时间段同一个CPU上交替执行,看起来好像是同时执行的。例如,多个线程在同一时间内运行。
  • 串行:指多个任务按照顺序依次执行,一个任务完成后才能执行下一个任务。例如,单线程程序就是串行执行的。
  • 并行:多个处理器或多核处理器同时处理多个任务,必须需要有多个处理器或者多核 CPU 才能实现,否则只能是并发。例如,多个线程在不同的处理器或者 CPU 上运行。

并发、串行、并行的区别

  • 执行方式:并发和串行都在单个处理器上执行,但是并发是多个任务交替执行,串行是按照顺序依次执行;并行需要多个处理器或多核 CPU 才能实现。
  • 性能:并发和并行都可以提高程序的性能和响应速度,但是并发需要考虑线程安全和死锁等问题;串行虽然简单稳定,但是无法充分利用多核 CPU 的优势。
  • 实现方式:并发可以使用多线程技术实现;串行只能使用单线程实现;并行需要多个处理器或多核 CPU 才能实现。

并发编程的三要素

  • 原子性:指一个或多个操作要么全部执行成功,要么全部执行失败
  • 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行(处理器可能会对指令进行重排序)

多线程和并发

多线程是指在同一时间内运行多个线程来完成多个任务。多线程可以提高程序的性能和响应速度,因为它们可以同时执行多个任务。

并发是指在同一时间内执行多个任务的能力。并发可以通过使用多线程来实现,但也可以通过其他方式实现,例如使用异步编程或事件驱动编程。

因此,多线程是实现并发的一种方式,但并发不一定需要使用多线程。另外,多线程编程中需要考虑的问题,例如线程安全和死锁等,在并发编程中同样需要考虑

1.3. 线程的实现方式

        多线程是提高程序性能和响应速度的重要手段,Java 中有多种实现方式, 

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 使用 Callable 和 Future
  • 使用线程池

1.3.1. 继承 Thread 类

  • 介绍:通过继承 Thread 类来实现多线程。
  • 示例代码:展示如何继承 Thread 类并重写 run() 方法。
  • 优点:实现简单,易于理解。
  • 缺点:无法继承其他类,因为 Java 不支持多重继承。
class MyThread extends Thread {
    public void run() {
        // 执行需要的代码
    }
}

MyThread thread = new MyThread();
thread.start();

1.3.2. 实现 Runnable 接口

  • 介绍:通过实现 Runnable 接口来实现多线程。
  • 示例代码:展示如何实现 Runnable 接口并重写 run() 方法。
  • 优点:可以继承其他类,因为 Java 支持实现多个接口。
  • 缺点:需要创建 Thread 对象来启动线程。
  • 步骤:
     - 自定义线程类实现Runnable接口
     - 实现run()方法,编写线程体
     - 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
class MyRunnable implements Runnable {
    public void run() {
        // 执行需要的代码
    }
}

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

1.3.3. 使用 Callable 和 Future

  • 介绍:通过使用 Callable 和 Future 接口来实现多线程。
  • 示例代码:展示如何使用 Callable 和 Future 接口来创建和启动线程。
  • 优点:可以获取线程执行的返回值。
  • 缺点:相比于前两种方式,实现稍微复杂一些。
  •  步骤:
     - 实现Callable接口,先要返回值类型
     - 重写call()方法,需要抛出异常
     - 创建目标对象
     - 创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
     - 提交执行:Future<Boolean> res = ser.submit(t1);
     - 获取结果:boolean r1 = res.get();
     - 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;

// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        // 线程执行体
        for (int i = 0; i < 10; i++) {
            System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
        }

        return true;
    }

    public static void main(String[] args) throws ExecutionException,
        InterruptedException {
        // main线程,主线程

        // 创建线程实现类对象
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        // 创建执行服务,参数是线程池线程数量
        ExecutorService ser = Executors.newFixedThreadPool(2);
        // 提交执行
        Future<Boolean> res = ser.submit(thread);
        Future<Boolean> res2 = ser.submit(thread2);
        // 获取结果
        boolean r1 = res.get();
        boolean r2 = res2.get();
        // 关闭服务
        ser.shutdownNow();
    }
}

1.3.4. 使用线程池

  • 介绍:通过使用线程池来实现多线程。
  • 示例代码:展示如何使用 Executor 和 ExecutorService 接口来创建和管理线程池。
  • 优点:可以重用线程,避免了频繁创建和销毁线程的开销。
  • 缺点:需要对线程池的大小进行合理配置,否则可能会导致性能问题。
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() {
    public void run() {
        // 执行需要的代码
    }
});

1.4.线程的状态

  • 新建(New):当线程对象创建后,线程处于新建状态。
  • 运行(Runnable):当调用线程的 start() 方法后,线程处于就绪状态,等待 CPU 调度执行。
  • 阻塞(Blocked):当线程等待某个条件(如 I/O 操作、锁)时,线程处于阻塞状态。
  • 等待(Waiting):当线程等待某个条件的唤醒(如调用 wait() 方法)时,线程处于等待状态。
  • 超时等待(Timed Waiting):当线程等待某个条件的唤醒,但是等待一定的时间后会自动唤醒(如调用 sleep() 方法或者带超时参数的 wait() 方法)时,线程处于超时等待状态。
  • 终止(Terminated):当线程执行完成或者出现异常时,线程处于终止状态。

1.5. 线程常用方法

  • start():启动线程。
  • run():线程执行的代码。
  • join():等待线程执行完毕。
  • sleep():使线程休眠一段时间。
  • interrupt():中断线程的执行。
  • wait() 使当前线程等待另一个线程发出通知。
  • notify() 通知等待的线程继续执行。
  • setPriority (int newPriority)、getPriority() 改变、获取线程的优先级。

1.5.1 sleep()

sleep() 方法可以使当前线程暂停指定的时间。例如:

try {
    Thread.sleep(1000); // 暂停 1 秒钟
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.4.2 join()

join() 方法可以等待指定的线程执行完毕。例如:

Thread thread1 = new Thread(() -> {
    // 执行需要的代码
});

Thread thread2 = new Thread(() -> {
    // 执行需要的代码
});

thread1.start();
thread2.start();

try {
    thread1.join(); // 等待 thread1 执行完毕
    thread2.join(); // 等待 thread2 执行完毕
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.5.3 yield()

暂停当前正在执行的线程对象,并执行其他线程,yield() 方法可以让出 CPU 时间片,使其他线程有机会运行。

例如:

Thread.yield();

yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。

1.5.4.wait() 和 notify() 

在多线程程序中,可以使用 wait()notify() 方法来协调多个线程之间的操作。wait() 方法可以使当前线程等待另一个线程发出通知,而 notify() 方法可以通知等待的线程继续执行。

public class Message {
    private String content;
    private boolean available = false;

    public synchronized String getContent() {
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        available = false;
        notifyAll();
        return content;
    }

    public synchronized void setContent(String content) {
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.content = content;
        available = true;
        notifyAll();
    }
}

public class Main {
    public static void main(String[] args) {
        Message message = new Message();
        // 创建两个线程来发送和接收消息
        new Thread(() -> {
            message.setContent("Hello");
        }).start();
        new Thread(() -> {
            System.out.println(message.getContent()); // 输出 Hello
        }).start();
    }
}

1.5.5.-interrupt()方法和stop()方法 

- JDK提供的stop方法已废弃,不推荐使用。

- 推荐线程自动停止下来,建议使用一个标识位变量进行终止,当flag=false时,则终止线程运行。

public class DemoInterrupt {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
        t.interrupt();
        //        t.stop(); //强行终止线程
        //缺点:容易损坏数据  线程没有保存的数据容易丢失
    }
}
class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            // 睡眠1天
            Thread.sleep(1000 * 60 * 60 * 24);
        } catch (InterruptedException e) {
        //            e.printStackTrace();
        }
        //1天之后才会执行这里
        System.out.println(Thread.currentThread().getName() + "---> end");
 
    }
}

1.5.6. setPriority (int newPriority)、getPriority()

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。 - 线程的优先级用数据表示,范围1~10。 - 线程的优先级高只是表示他的权重大,获取CPU执行权的几率大。 - 先设置线程的优先级,在执行start()方法

public class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程优先级:"
            + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"a");
        Thread thread2 = new Thread(myThread,"b");
        Thread thread3= new Thread(myThread,"c");
        Thread thread4= new Thread(myThread,"d");
        thread3.setPriority(Thread.MAX_PRIORITY);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.NORM_PRIORITY);
        thread4.setPriority(8);
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

结果如下:

c线程优先级:10
b线程优先级:5
a线程优先级:1
d线程优先级:8

1.6.线程调度

  • 线程调度模型

    • 均分式调度模型:所有的线程轮流使用CPU的使用权,平均分配给每一个线程占用CPU的时间。

    • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个线程来执行,优先级高的占用CPU时间相对来说会高一点点。

    Java中JVM使用的就是抢占式调度模型

  • getPriority():获取线程优先级

  • setPriority:设置线程优先级

2.进阶多线程

2.1.多线程引发问题

多线程编程能够提高程序的性能和响应能力,但同时也会带来一些问题。主要包括以下几个方面:

  1.竞态条件(Race Condition):当多个线程同时访问共享资源时,由于线程执行顺序的不确定性,可能会导致程序的输出结果出现错误。例如,多个线程同时对一个计数器进行自增操作,如果没有进行同步,可能会导致计数器的值不正确。

        可以使用 synchronized 关键字来解决竞态条件问题。

  2.死锁(Deadlock):当多个线程相互等待对方释放所占用的资源时,可能会陷入死锁状态,无法继续执行。例如,线程 A 占用了资源 1,等待资源 2,而线程 B 占用了资源 2,等待资源 1,两个线程都无法继续执行。

        可以使用锁和条件变量来解决死锁问题。

  3.饥饿(Starvation):当某些线程由于竞争共享资源失败而无法继续执行时,可能会出现饥饿问题。例如,如果一个线程在一个高负载的系统中请求资源,它可能会等待很长时间才能获得所需的资源。

       4.上下文切换:当多个线程同时运行时,操作系统需要进行上下文切换,这会消耗一定的系统资源。如果线程数量过多,上下文切换的开销可能会超过程序本身的开销

        5.线程安全:在多线程程序中,需要确保共享资源的安全性。可以使用锁和原子变量来实现线程安全。

        6.性能优化:在多线程程序中,需要考虑性能优化问题。可以使用线程池和并发集合来提高程序性能。

2.2.多线程解决方法

2.2.1. 锁机制

  • 定义:锁机制是解决多线程之间互斥访问共享资源的一种方式。
  • 实现方式:使用 synchronized 关键字、ReentrantLock 类或 ReadWriteLock 接口等实现锁机制。
  • 常用工具:Lock、Condition、Semaphore、ReadWriteLock 等。

2.2.3. 线程池

  • 定义:线程池是管理和调度多个线程的一种机制,可以避免频繁创建和销毁线程带来的性能开销。
  • 实现方式:使用 Executors 类或 ThreadPoolExecutor 类创建和管理线程池。
  • 常用参数:核心线程数、最大线程数、任务队列、拒绝策略等。

2.2.4. 并发工具类

  • 定义:并发工具类是解决并发编程中常见问题的一种工具,例如阻塞队列、计数器、信号量等。
  • 实现方式:使用 Java.util.concurrent 包中提供的工具类实现并发编程。
  • 常用工具:ArrayBlockingQueue、CyclicBarrier、Semaphore、CountDownLatch 等。

3.面试题

3.1.sleep()和wait()有什么区别?

两者都可暂停当前线程

  • 所在类不同:sleep()时Thread类的静态方法,wait()是Object类的方法
  • 释放锁:sleep()不会释放锁,wait()会释放锁
  • 用途不同:wait()通常用于线程间通信,sleep()通常用于暂停线程
  • 结果不同:sleep()方法执行完成后,线程会再次进入就绪态;wait()方法被notify()唤醒后,线程会进入同步队列重新抢占锁

3.2.如何停止一个处在运行态的线程

  1. 该线程的run()方法执行结束后线程自动终止
  2. 使用stop()方法强制终止,但一般很少这么用
  3. 使用interrupt()方法中断线程(其流程为,设置一个中断标志位,调用interrupt()方法通知系统请求关闭线程,待系统在适合的时间自行中断)

3.3.interrupt()、interrupted()、isInterrupted()方法的区别

  • interrupt()方法和isInterrupted()方法都是实例方法,通过Thread对象调用;interrupted()则是静态方法,通过Thread类调用,三个方法都是与线程中断有关的
  • interrupt()方法:用来设置中断标志位,通知系统请求结束线程,由系统决定具体何时中断

此时如果线程在阻塞状态:
那么就会抛出InterruptedException异常,并重置中断标志位

如果线程不在阻塞状态:
使用Thread.interrupted()判断当前中断标志位是否被设置,并重置中断标志位
使用Thread.currentThread.isInterrupted()判断当前中断标志位是否被设置,不重置中断标志位

3.4.系统创建的线程过多会有什么影响

  • 线程的生命周期开销非常高
  • 消耗过多的CPU
  • 降低JVM的效率

4.写在最后

以上就是我对多线程的个人简介,后续会不断完善更新,与大家共勉

鸣谢

[1] https://blog.csdn.net/zdl66/article/details/126297036

[2] https://blog.csdn.net/m0_46233999/article/details/118702235

[3] https://blog.csdn.net/qq_29141913/article/details/125964815#3_16

[4] https://blog.csdn.net/YQQAGH178/article/details/119828128

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/28656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker 网络理论知识点 - CNM 和命名空间

Network 目录 1 network namespace1.1 动手小实验 2 回到 docker2.1 driver and docker02.2 network2.3 网桥 docker0 3 总结 1 network namespace 1.1 动手小实验 网络命名空间。linux kernel 提供的网络虚拟化的功能。创建多个隔离的网络空间。每个空间内 firewall, ether …

【taro react】---- 解决H5接入uni-app版本的IM

1. 问题 由于项目开发比较紧张&#xff0c;腾讯 IM 的接入就使用了 TUIKit 含UI集成方案&#xff0c;遇到的问题&#xff0c;uni-app的UI本来就是一个单独的项目&#xff0c;需要集成到现有的 Taro React 中&#xff0c;就只能作为一个独立的项目&#xff0c;不跳转时不影响原有…

在十四届蓝桥杯开赛前一星期开始复习

文章目录 十三届蓝桥杯国赛原题1.20222.钟表3卡牌4最大数字4.5 Dijkstra算法5出差 十三届蓝桥杯国赛原题 1.2022 #include<iostream> using namespace std;long long int f[2023][11][2023]; //表示前2022个物品选择10个物品&#xff0c;体积总和为2022的方案个数 ,,数…

入门编程其实也简单

随着信息技术的快速发展&#xff0c;编程已经成为一个越来越重要的技能。那么&#xff0c;我们该如何入门编程呢&#xff1f; 编程是指使用计算机语言编写计算机程序的过程。计算机程序是一系列指令的集合&#xff0c;这些指令告诉计算机要执行的操作。编程的目的是创建计算机…

Unity编辑器扩展-第二集-按钮排序/分组/放入右键菜单

第一集链接&#xff1a;Unity编辑器扩展-第一集-在菜单栏加入自己的按钮_菌菌巧乐兹的博客-CSDN博客 一、本节目标效果展示 1.按钮排序 变成 2.按钮分组 仔细看&#xff0c;有个灰色的杠杠 3.放入右键菜单 4.皮一下 二、按钮排序具体流程 第一集讲&#xff0c;如果想放入…

阿里云PAIx达摩院GraphScope开源基于PyTorch的GPU加速分布式GNN框架

作者&#xff1a;艾宝乐 导读 近期阿里云机器学习平台 PAI 团队和达摩院 GraphScope 团队联合推出了面向 PyTorch 的 GPU 加速分布式 GNN 框架 GraphLearn-for-PyTorch(GLT) 。GLT 利用 GPU 的强大并行计算性能来加速图采样&#xff0c;并利用 UVA 来减少顶点和边特征的转换和…

4.4.2 译码器

1. 学习基础知识&#xff1a;首先&#xff0c;我会了解译码器的基本概念、原理和应用。通过阅读教科书、参考资料或在线资源&#xff0c;我会学习译码器的工作原理、不同类型的译码器以及它们在电子系统中的应用场景。 2. 研究示例和练习题&#xff1a;为了更好地理解译码器的…

【Spring】透过Spring源码查看Bean的命名转换规则

近期在写Spring项目的时候&#xff0c;需要通过注解的形式去替代之前直接将Bean存放在Spring容器这种方式&#xff0c;以此来简化对于Bean对象的操作&#xff0c;但是这样无法通过准确的Id去获取到相应的Bean对象了 测试观察 首先&#xff0c;如果要将指定的对象存放到Spring中…

Unity入门6——光源组件

一、参数面板 二、参数介绍 Type&#xff1a;光源类型 Spot&#xff1a;聚光灯 Range&#xff1a;发光距离Spot Angle&#xff1a;光锥角度Directional&#xff1a;方向光Point&#xff1a;点光源Area&#xff08;Baked Only&#xff09;&#xff1a;面光源 仅烘焙。预先算好&…

操作教程:如何正确配置让EasyNVR级联至EasyNVS平台?

EasyNVS是EasyNVR的云管理平台&#xff0c;可实现内网监控上云&#xff0c;视频汇聚等功能。近期经常有用户咨询EasyNVR如何级联至EasyNVS平台进行云端统计和管理&#xff0c;在今天的文章中&#xff0c;我们来详细介绍一下。 1、配置EasyNVS 1&#xff09;运行EasyNVS之前&a…

回归预测 | MATLAB实现基于GRU-AdaBoost门控循环单元结合AdaBoost多输入单输出回归预测

回归预测 | MATLAB实现基于GRU-AdaBoost门控循环单元结合AdaBoost多输入单输出回归预测 目录 回归预测 | MATLAB实现基于GRU-AdaBoost门控循环单元结合AdaBoost多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于GRU-AdaBoost门…

C++【STL】之vector的使用

文章目录&#xff1a; vector介绍vector使用1. 默认成员函数1.1 默认构造1.2 拷贝构造1.3 析构函数1.4 赋值重载 2. 迭代器2.1 正向迭代器2.2 反向迭代器 3. 容量操作3.1 获取空间数据3.2 空间扩容3.3 大小调整3.4 空间缩容 4. 数据访问4.1 下标随机访问4.2 获取首尾元素 5. 数…

基于Java新生报到系统设计与实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

Spring源码解密--事务篇

文章目录 一、事务的实现方式1、JDBC2、Spring基于xml配置编程式事务声明式事务 二、源码设计1、TransactionManager1&#xff09;TransactionManager2&#xff09;PlatformTransactionManager3&#xff09;ReactiveTransactionManager 2、TransactionDefinition3、Transaction…

Qt编写onvif工具(搜索/云台/预置位/OSD/录像存储)

一、前言 从最初编写这个工具开始的时间算起来&#xff0c;至少5年多&#xff0c;一直持续完善到今天&#xff0c;这个工具看起来小也不小大也不大&#xff0c;但是也是经历过无数个现场的洗礼&#xff0c;毫不夸张的说&#xff0c;市面上能够遇到的主流的厂商的设备&#xff…

攻防世界-Crypto-easychallenge

题目描述&#xff1a;将文件下载下来&#xff0c;只有一个pyc文件 1. 思路分析 先向chatgpt问下什么是pyc文件&#xff1a; OK&#xff0c;这里简单总结下&#xff1a; 1. pyc文件是python源码编译后的生成的二进制文件 2. 通过一些库可以逆向出pyc的源代码 那么我们需要做…

科一容易忘、容易混的点(二)

注意落石 高速应急车道用途&#xff1a;主要用于发生事故或故障时停车&#xff0c;以及专为救险所用的车道&#xff0c;被誉为高速公路的“生命通道” 注意 注意 扣9份场景&#xff1a; 城市快速路上 违法停车&#xff1b; 注意 交通事故逃逸&#xff0c; 不一定吊销驾驶证&a…

简单的一批的DockerFile构建(内附超详细docker学习笔记)

目录 介绍 DockerFile常用保留字指令 演示自定义构建java8版本centos docker专用学习笔记 超全 介绍 总结: 从应用软件的角度来看&#xff0c;Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段&#xff0c; * Dockerfile是软件的原材料 * Docker镜像是软件…

华为组播实验pim-dm

组播源配置&#xff1a; R1: [r1]dis current-configuration [V200R003C00] sysname r1 snmp-agent local-engineid 800007DB03000000000000 snmp-agent clock timezone China-Standard-Time minus 08:00:00 portal local-server load flash:/portalpage.zip drop illegal-mac…

Java调用Midjourney进行AI画图原生版抓包实现支持中文

用途介绍 Midjourney是一个目前优秀的AI画图工具&#xff0c;不挂梯无法直接访问 本代码主要用于搭建镜像站使用 适合人群 本代码不适合新手&#xff0c;建议使用过okhttp、且具有二开能力的同学使用~ 实现原理 通过调用发送信息接口发送请求&#xff0c;通过轮询房间消息…