java语法(二)线程并发、Juit单元测试、注解、反射机制、JVM

news2024/11/24 20:09:30

文章目录

  • 线程并发
    • 多线程
      • 多线程的创建
      • Thread常用API
    • 线程同步与通信
      • 线程同步:单例模式的三种写法
        • 同步代码块
        • 同步方法
        • Lock锁
      • 线程通信
  • 线程池
    • 获取线程池对象
    • ThreadPoolExecutor
      • 线程池处理runnable任务
      • 线程池处理callable任务
    • Executors
    • 定时器
      • Timer
        • 调度可重复执行任务
        • 取消定时器
      • ScheduleExecutorService
    • 并发与并行
    • 线程的生命周期
      • Runnable
      • 阻塞状态
        • Blocked:没获得锁被阻塞
        • Waiting等待状态
      • Time Waiting计时等待状态
    • 进程状态之间的转换
      • sleep和wait的区别
  • Junit单元测试框架
  • 反射
    • 反射获取Class类的全部成分
      • 获取Class类对象
      • 获取构造器(Constructor)、成员(Field)、成员函数(Method)
    • 反射的作用
  • 注解

线程并发

多线程

多线程的创建

  1. 方法一:继承Thread类,创建步骤:
    • 定义子类MyThread继承Thread,重写run()方法
    • 创建MyThread类对象
    • 调用线程对象的start()方法启动线程(启动后执行的是run()方法)

这种创建方式因为已经继承了Thread类,无法继承其他类,不利于拓展。

  1. 方法二:声明一个实现Runnable接口的类。
    • 定义定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。
    • 创建MyRunnable对象
    • 把MyRunnable对象交给Thread处理
    • 调用Thread对象的start方法启动

方法二只实现接口,可以继续继承类和实现接口,扩展性更强。但缺点是编程多一层包装(runnable对象还需要再传给thread构造thread对象)
在这里插入图片描述
方法一实现:

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            System.out.println("子线程执行输出:" + i);
        }
    }
}
 Thread t1 = new MyThread();
 t1.start();

方法二实现:

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
});
for (int i = 0; i < 10; i++) {
    System.out.println("主线程:" + i);
}

当然,可以用lambda表达式简写。

 Thread t = new Thread(() -> {
     for (int i = 0; i < 10; i++) {
         System.out.println( "子线程" + i);
     }
 });
 for (int i = 0; i < 10; i++) {
     System.out.println("主线程:" + i);
 }
  1. 方式3
    前两种创建方式都存在一个问题:
    • 他们重写的run()方法均不能直接返回结果
    • 不适合需要返回线程执行结果的业务场景

jdk5利用Callable、FutureTask接口实现上述功能。

创建步骤:

  • 定义类实现Callable接口,重写call方法,封装要做的事情。
  • 用FutureTask把Callable对象封装成线程任务对象。
  • 把线程任务对象交给Thread处理
  • 调用Thread的start方法启动线程,执行任务。
  • 线程执行完毕后,通过FutureTask的get()方法去获取任务执行的结果。

方法三的实现:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadDemo3 {
    public static void main(String[] args) {
        Callable<String> call1 = new MyCallable(100000);
        FutureTask<String> f1 = new FutureTask<>(call1);
        Thread t1 = new Thread(f1);
        t1.start();

        Callable<String> call2 = new MyCallable(100000);
        FutureTask<String> f2 = new FutureTask<>(call2);
        Thread t2 = new Thread(f2);
        t2.start();

        try {
            // 如果f1没有执行完毕,那么get这里会等待,直至完成
            String r1 =  f1.get();
            System.out.println("第一个结果:" + r1);
        } catch (Exception e) {
            e.printStackTrace();
        }
            // 如果f2没有执行完毕,那么get这里会等待,直至完成
        try {
            String r2 =  f2.get();
            System.out.println("第二个结果:" + r2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<String> {
    private int n;
    public MyCallable (int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "子线程执行的结果是:" + sum;
    }
}

Thread常用API

在这里插入图片描述
在这里插入图片描述

线程同步与通信

当多线程访问共享资源时,可能出现线程安全问题。解决线程安全问题的方法有线程同步和线程通信。

线程同步:单例模式的三种写法

线程同步的思想是对共享资源加锁,将多个线程实现先后依次访问共享资源,这样就解决了线程安全问题。

线程同步的方式:

  • 同步代码块
  • 同步方法
  • Lock锁

同步代码块

  • 作用:把出现线程安全问题的核心代码给上锁。
  • 原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。

synchronized(同步锁对象){
操作共享资源的代码
}

锁对象规范:

  • 建议使用共享资源作为锁对象
  • 对于实例方法建议使用this作为锁对象。
  • 对于静态方法建议使用字节码(类名.class)对象作为锁对象。

同步代码块实现的懒汉单例模式:
核心:私有化构造函数,静态化单例成员,在获取单例的静态方法中,如果检测到实例未创建,使用synchronized构建同步代码块,因为是静态方法,所以使用类的字节码(类名.class)对象作为synchronized的锁对象。

class SingleInstance {
    private SingleInstance() {
    }
    private static SingleInstance singleInstance;
    public static SingleInstance getInstance() {
        if (singleInstance == null) {
            synchronized (SingleInstance.class) {
                if (singleInstance == null) {
                    singleInstance = new SingleInstance();
                }
            }
        }
        return singleInstance;
    }
}

同步方法

在这里插入图片描述
基于同步方法实现的懒汉单例:
核心:私有化构造、静态化单例对象
获取实例的静态方法加synchronized修饰。

class Single {
    private Single(){
        
    }
    private static Single single;
    public static synchronized Single GetInstance() {
        if (single == null) {
            single = new Single();
        }
        return single;
    }
}

同步方法的底层其实是隐式锁对象,只是锁的范围是整个方法代码,如果方法是实例方法,同步方法默认用this作为锁对象。

从性能上讲,同步代码块锁的范围更小,性能更高。

java中静态内部类不会自动初始化,只有在调用静态内部类的方法时才会加载静态内部类。

利用这种静态内部类,我们可以实现一个比使用同步代码块和同步方法,性能上更加优秀的懒汉单例。

核心:私有化构造函数,构建静态内部类,类中成员初始化时调用构造函数,使用static决定它的全局性 ,使用final,决定它只会初始化一次。在获取单例的方法中,返回内部类的成员。

class SingleByInner{
    private SingleByInner() {
		
    }
    static class Inner {
        private static final SingleByInner INSTANCE = new SingleByInner();
    }
    public static SingleByInner getInstance() {
        return Inner.INSTANCE;
    }
}

Lock锁

  • 为了更加清晰的表达如何加锁和释放锁,JDK5之后提供了一个新的锁对象Lock,更加灵活,方便。
  • Lock实现提供比使用synchronize方法和语句可以获得更广泛的锁定操作。
  • Lock是接口不能直接实例化,这里采用它的实现类ReetrantLock来构建Lock锁对象。

线程通信

什么是线程通信、如何实现?

  • 所谓线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现。
  • 线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做。

线程通信常见模型

  • 生产者与消费者模式:生产者线程负责生成数据,消费者线程负责消费数据。
  • 要求:生产者生产完数据后,唤醒消费者,然后等待自己;消费者消费完数据后,唤醒生产者,然后等待自己。

线程通信的关键是object类的等待和唤醒方法在这里插入图片描述上述所有方法应该使用当前同步锁对象进行调用。

this.notifyAll();  // 唤醒所有线程
this.wait();  // 锁对象,让当前线程进入等待。

经典面试题:sleep()和wait()的区别是什么?
1、来自不同类:sleep()来着Thread,wait()来自Object。
2、sleep()不会释放锁,wait()会释放锁。
3、使用范围不同:wait,notify和notifyAll只能在同步控制方法或者同步代码块中使用,sleep可以在任何地方使用。

线程池

线程池是一个可以复用线程的技术。因为创建新线程的开销很大,使用线程池可以重复利用线程,可提高程序性能。
在这里插入图片描述

获取线程池对象

JDK5.0起提供了代表线程池的接口:ExecutorService
如何获取线程池对象?

  • 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象,这种方法最为灵活。

  • 方式二:使用Executor(线程池的工具类)调用方法返回不同特点的线程池对象。

ThreadPoolExecutor

在这里插入图片描述
临时线程什么时候创建?

  • 新任务提交时,核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
    什么时候会开始拒绝任务?
  • 核心线程和临时线程都在忙,任务队列也满了,新的任务过来时才会开始拒绝任务。

在这里插入图片描述

线程池处理runnable任务

        ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        Runnable target = () -> {
            try {
                Thread.sleep(100000);
                System.out.println(Thread.currentThread().getName() + "输出");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        // 三个核心线程
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        // 五个在等待队列
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        // 等待队列满了,新加两个临时线程
        pool.execute(target);
        pool.execute(target);
        
        // 拒绝任务,抛出异常
        pool.execute(target);

    }

线程池处理callable任务

在这里插入图片描述
execute 执行runnable类任务,submit处理callable任务。

Executors

Executors是jdk内置的线程池工具类,可以返回下边四种类型的线程池:

在这里插入图片描述

定时器

Timer

Timer和TimerTask是用于在后台线程中调度任务的java util类。简单地说,TimerTask是要执行的任务,Timer是调度器。

1.自定义一个类继承于TimerTask的类,并重写其run()方法即可。

2.可以采取匿名类的形式,直接重写其run()方法。

TimeTask有一抽象方法run(),其作用就是用来放我们处理的逻辑任务。

Timer有一schedule()方法,重载参数和另外两个方法如下表:
可以看到schedule()可以接收Data参数,指定某个时刻执行,也可接收long类型的毫秒参数,延迟多少毫秒执行。
在这里插入图片描述

    public static void usingTimer() {
        Timer timer = new Timer("Timer");
        long delay = 2000L;
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("Task performed on: " + new Date() + "n" +
                        "Thread's name: " + Thread.currentThread().getName());
            }
        }, delay, 3*1000);  // 每3秒执行一次定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(new Date());  
            }
        },delay); // 只执行一次
    }

调度可重复执行任务

有两种方式:

  • 固定延迟:schedule()方法还有两个重载,每个重载都使用一个额外的period参数来表示以毫秒为单位的周期性。
  • 固定频率:有两个scheduleAtFixedRate()方法,它们的周期也是以毫秒为单位的。

注意:
1、如果一个任务的执行时间超过了执行周期,那么无论我们使用固定延迟还是固定速率,它都会延迟整个执行链。
2、更有甚者如果定时调度器中一个定时任务出现异常,会同时影响其他任务的进行。
这就是使用Timer定时器的缺点所在,Timer本身是单线程的,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。可能因为其中的某个任务异常,影响后续任务。

取消定时器

1、调用Timer.cancel()方法。
2、在TimerTask的run()方法中使用cancle取消。

ScheduleExecutorService

ScheduleExecutorService内部是线程池来处理定时任务,如果某个任务失败,那么这个线程会挂掉,不会影响其他线程的任务。
在这里插入图片描述

并发与并行

  • 正在运行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实是并发与并行同时进行的。

并发的理解:

  • CPU同时处理线程的数量有限。
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程在同时执行,这就是并发。

并行的理解:

  • 在同一个时刻,多个线程同时执行

线程的生命周期

线程的生命周期主要有以下6种状态:

  • New(新创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Waiting(等待)
  • Timed Waiting(计时等待)
  • Terminated(被终止)

在这里插入图片描述
New表示线程被创建,尚未启动的状态,即new Thread(),新建一个线程但是还没有执行start()方法。当执行start()后,就进入了Runnable状态。

Runnable

Java中的Runnable状态对应操作系统线程状态中的两种状态分别是RunningReady,也就是说Java中处于Runnable状态的线程可能是正在执行,也可能是在等待CPU分配资源。

阻塞状态

Blocked(被阻塞)、Waiting(等待)、TimedWaiting(计时等待)这三种状态统称为阻塞状态。

Blocked:没获得锁被阻塞

从Runnable状态进入到Blocked状态只有一种途径,那就是当进入到synchronized代码块中时,未能获得相应的锁。

相应的,当Blocked状态的线程获取到锁时,此线程就会进入到Runnable状态中参与CPUT资源的抢夺。

Waiting等待状态

waiting状态有三种情况:

  • 当线程中调用了没有设置timeout参数的object.wait()方法。
  • 当线程调用了没有设置timeout参数的thread.join()方法。
  • 当线程调用了LockSupport.park()方法。

Blocked与Waiting的区别

  • Blocked是在等待其他线程释放锁
  • Waiting则是在等待某个条件,比如join的线程执行完毕,或者notify()/notifyAll().

Time Waiting计时等待状态

Time Waiting状态与Waiting状态非常相似,其中的区别就在于是否有时间的限制,在Timed Waiting 状态时会等待超时,之后由系统唤醒,或者也可以提前被通知唤醒如notify

进程状态之间的转换

WaitTimeWaiting状态被notify或者notify_all后,如果拿到锁,那么进入Runnable,如果没有拿到锁,则进入BlockedRunnable执行不带时间参数的wait进入waiting状态,执行带时间参数的wait或者sleep后进入TimeWaiting状态。

sleep和wait的区别

  • 来自不同的对象:sleep来自Thread,wait来自Object
  • 使用场景不同:wait只能在同步代码块或者同步方法中使用,执行wait前应该使用notify或者notify_all唤醒其他线程。执行wait后会释放自己的锁。sleep则没有使用场景的限制,sleep执行后并不会释放锁。

Junit单元测试框架

编写Junit测试的步骤:

  1. 一般而言IDEA整合了Junit框架,不需要另外导入,但是如果IDEA没有整合,需要手工导入Junit的两个Jar包。
  2. 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法
  3. 在测试方法上使用@Test注解:标注该方法是一个测试方法。
  4. 在测试方法中完成被测试方法的预期正确性测试。
  5. 选中测试方法,选择“Junit运行”,如果测试良好则是绿色,如果测试失败,则是红色。

例子:
对两个业务代码的正确性编写单元测试:
业务用例:

public class UserService {
    public String loginName(String loginName, String passWard) {
        if ("admin".equals(loginName) && "123456".equals(passWard)) {
            return "登录成功";
        } else {
            return "用户或者密码有问题";
        }
    }

    public void selectName() {
        System.out.println(10 / 0);
    }
}

测试案例:

public class TestUserService {
    /**测试方法
     * 1、必须是公开的,无参数,无返回值的方法
     * 2、测试方法必须使用@Test注解标记
     */

    @Test
    public void testLoginName() {
        UserService userService = new UserService();
        String rs = userService.loginName("admin", "123456");
        Assert.assertEquals("用户或者密码有问题", "登录成功", rs);
    }

    @Test
    public void testSelectNames() {
        UserService userService = new UserService();
        userService.selectName();
    }
}

测试结果:
在这里插入图片描述

反射

反射的概述:

  • 反射是指对于任何一个Class类,在“运行的时候”都可以直接得到这个类的全部成分。
    • 在运行时,可以直接得到这个类的构造对象:Constructor
    • 在运行时,可以直接得到这个类的成员变量对象:Field
    • 在运行时,可以直接得到这个类的成员方法对象:Method
  • 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制

反射的关键:
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。
HelloWorld.java -> javac -> HelloWorld.class Class c = HelloWorld.class;

反射获取Class类的全部成分

获取Class类对象

一、反射第一步:获取Class对象,有下边三种方法:

在这里插入图片描述

  • 在源代码阶段:Class类中的静态方法:forName(String className)
  • 在Class对象阶段,通过类名.class
  • 在Runtime运行时阶段,通过对象.getClass()

回顾我们在编写同步代码块实现的单例对象时。便是通过了上述的方法二,获取到类对象来作为锁的互斥资源:
synchronized (SingleInstance.class){}

public static SingleInstance getInstance() {
   if (singleInstance == null) {
       synchronized (SingleInstance.class) {
           if (singleInstance == null) {
               singleInstance = new SingleInstance();
           }
       }
   }

下边我们编写一个Student类,尝试用上边的三种方法获取到Class对象:

        // 1、Class.forName(全限名) 全限名:包名 + 类名
        Class C1 = Class.forName("com.heima2.model.Student");
        System.out.println(C1);

        // 2、通过 类名.class
        Class C2 = Student.class;
        System.out.println(C2);
        
        // 3、通过 对象.getClass
        
        Student s = new Student();
        Class C3 = s.getClass();
        System.out.println(C3);

三个打印结果相同,因为编辑器只会编译出一份类对象。

获取构造器(Constructor)、成员(Field)、成员函数(Method)

Constuctor

通过class对象的.getConstructors()获取全部构造器
获取每个构造器,打印它的名字+构造器的参数个数。

        Student s = new Student();
        Class C3 = s.getClass();

        Constructor[] constructors = C3.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.getName() + "-->" + c.getParameterCount());
        }

使用.getConstructor(T ...)方法按照参数获取指定的构造器:

Constructor con1 = C3.getConstructor();  // 获取无参构造器
Constructor con2 = C3.getConstructor(String.class, String.class);  // 获取有参构造器

注意:上边两种方法无法获取到声明为private的构造器

如果要拿到private的构造器,需要使用.getDeclaredConstructor()方法

反射的作用

注解

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【连续介质力学】张量的谱表示和Cayley-Hamilton定理

张量的谱表示 基于特征多项式&#xff1a; 如果 T T T 是对称二阶张量&#xff0c;那么有三个实数特征值&#xff1a; T 1 , T 2 , T 3 T_1, T_2, T_3 T1​,T2​,T3​, 对应有特征向量&#xff1a; 主空间由特征向量构成的正交基 n ^ ( 1 ) , n ^ ( 2 ) , n ^ ( 3 ) \hat n^…

【自然语言处理】【大模型】BLOOM模型结构源码解析(单机版)

BLOOM模型结构源码解析(单机版) ​ 本文基于transformers中BLOOM模型代码来解析BLOOM的原理及实现。 相关博客 【自然语言处理】【大模型】BLOOM模型结构源码解析(单机版) 【自然语言处理】【大模型】极低资源微调大模型方法LoRA以及BLOOM-LORA实现代码 【深度学习】【分布式训…

Win32程序入口识别,定位回调函数,具体事件处理的定位,ESP寻址方式,压栈方式复习

本专栏上一篇文章带领大家编写了第一个Windows程序&#xff0c;并且带领大家了解了Windows消息机制&#xff0c;如果大家还是不了解的话&#xff0c;可以到我的另一篇专栏中深入学习一下windows消息&#xff0c;Windows消息循环的原理&#xff0c;点击这里就可以查看啦。 我们在…

Gateway网关详解及实践---SpringCloud组件(五)

Gateway网关详解及实践 一 简介1.1介绍1.2.GateWay核⼼概念1.3.GateWay核心功能 二Gateway入门案例2.1 gateway依赖2.2 gateway配置2.3 gateway测试 三Gateway面向服务的路由3.1.门案例问题3.2.gateway-server结合eureka步骤3.2.1.添加Eureka客户端依赖3.2.2.添加Eureka配置3.2…

MySQL进阶-MySQL体系结构和常见存储引擎的比较

本文介绍MySQL体系结构以及存储引擎&#xff08;InnoDB、MyISAM、Memory&#xff09;的比较 文章目录 MySQL 体系结构连接层服务层连接池系统管理和控制工具SQL接口解析器查询优化器缓存 引擎层存储层 存储引擎InnoDBMyISAMMemory区别 MySQL 体系结构 连接层 最上层是一些客户…

Rust 正式发布八周年纪念日 2023.5.15

图源&#xff1a;维基百科 目录 Rust 1. Rust的特点 1.1 安全性 1.2 并发性 1.3 性能 1.4 代码可读性 2. 使用场景 2.1 系统编程 2.2 Web开发 2.3 游戏开发 3. 与其他语言的对比 4. 代码示例 1. Hello, World! 2. 简单的函数 3. 变量绑定和变量类型推断 4. 结…

dayday60-120

目录 60 申论强化361 申论强化462 模考大赛错题63 言语真题164 言语真题65 言语真题366 判断真题1676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 60 申论强化3 1.公文题的格式不允许丢…

Vector - CAPL - CAN消息自动化设置

目录 canGetDataLength -- 获取CAN消息有效负载长度 代码示例 canOutputErrorFrame -- 模拟发送错误帧消息 代码示例 isStdId & isExtId -- 判断报文是标准帧&扩展帧 代码示例 mkExtId -- 将标准帧转换为扩展帧 代码示例 valOfId -- CAN消息的长值标识符 代…

基于SpringBoot的原创歌曲平台的设计与实现

背景 原创歌曲分享平台&#xff0c;为了随时随地查看原创歌曲分享信息提供了便捷的方法&#xff0c;更重要的是大大的简化了管理员管理原创歌曲分享信息的方式方法&#xff0c;更提供了其他想要了解原创歌曲分享信息及运作情况以及挑选方便快捷的可靠渠道。相比于传统原创歌曲…

六级备考33天|CET-SET6|六级口语|备考手册|考试样题|热门话题

目录 样题 1 The benefits of college athletics 2 The adverse impact of cheating on examinations 3 My view on working part-time during ones college years 4 Should students take part in extracurricular activities 5 If you choose overseas studies 6 The…

基于stm32物联网开发板(3)--SYN6288语音模块

基于stm32物联网开发板(3)–SYN6288语音模块 1.SYN6288语音模块展示示例 SYN6288语音模块 2.概述 SYN6288-A语音合成模块是一款性价比更高&#xff0c;效果更自然的一款中高端语音合技术 。 SYN6288-A通过异步串口接收待合成的文本&#xff0c;实现文本到声音&#xff08;TTS&…

SPL即将取代SQL?

先来看看两者的定义。 SQL&#xff1a;结构化查询语言。 SPL&#xff1a;结构化数据计算语言。 既然是比较文章&#xff0c;那必然是要突出一方的优势。 以下是SQL的痛点以及SPL的优点&#xff1a; 1.SQL缺乏离散性&#xff0c;集合化不彻底&#xff1b;SPL离散与集合充分结…

如何删除 Docker 镜像、容器和卷?

Docker 是一款常用的容器化平台&#xff0c;通过 Docker 可以将应用程序打包成一个独立的容器&#xff0c;方便地在不同的环境中运行。随着 Docker 的广泛使用&#xff0c;删除 Docker 镜像、容器和卷的操作也变得非常重要。在本文中&#xff0c;我们将介绍如何删除 Docker 镜像…

Python消费Kafka与优化

一.背景与问题 之前使用kafka-python库进行消费数据处理业务逻辑&#xff0c;但是没有深入里面的一些细节&#xff0c;导致会遇到一些坑。正常普通我们常见的一个消费者代码:(假设topic的分区数是20个) from kafka import KafkaConsumerbootstrap_servers [localhost:9092] g…

vim命令大全,非常详细,强烈建议收藏!

Vim是一款常用的文本编辑器&#xff0c;具有强大的功能和高度的可定制性。在本文中&#xff0c;我们将详细介绍Vim的常用命令&#xff0c;并提供相关的示例。如果您是初学者或已经熟练使用Vim&#xff0c;这篇文章都能为您提供帮助。 基本命令 以下是一些基本的Vim命令&#x…

一文足矣:Unity行为树

目录 前言 unity行为树简介 一个简单的敌人AI 正文 个人对行为树的理解 有限状态机与行为树 基本框架 BTNode DataBase 行为树入口 行为树的事件GraphEvent 发送事件 监听事件 脚本发送事件 行为树的管理&操作 一、操作单颗树 二、管理所有树 自定义Task任务 …

python字符串的三种定义方式

之前我们讲过 一些字符串的定义 但当时是说 被双引号包裹的就是字符串 其实并不是特别严谨 这个叫双引号的定义方式 也没错 也只有字符串会被双引号包裹 但还有其他的定义方式 这里 还是先说答案 三种定义方式分别是 单引号定义 双引号定义 三引号定义 参考代码如下 #单引定义…

《点云处理算法》——GROR配准

GROR配准方法&#xff08;实时性挺好&#xff09; 一、 效果展示二、VS运行2.1 github源码下载2.2 编译运行 三、后续集成 一、 效果展示 二、VS运行 最近和小伙伴交流&#xff0c;他发现一个好用的配准方法&#xff0c;放在这里实现一下 2.1 github源码下载 gror 2.2 编译…

hexo,typecho,wordpress,hugo的官网下载及介绍

Typecho Typecho是一个轻量级的PHP博客系统&#xff0c;它的优点在于易于安装、使用和管理。Typecho使用MySQL数据库来存储文章和评论&#xff0c;同时支持主题和插件的自定义。Typecho适用于个人博客、技术博客等&#xff0c;因为它的易用性和可扩展性较高。 WordPress Word…

分析SpringBoot 底层机制【Tomcat 启动分析+Spring 容器初始化+Tomcat 如何关联Spring 容器之手动实现

分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器之手动实现 目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器之手动实现 实现任务阶段1- 创建Tomcat, 并启动 说明: 分析代码实现 修改…