Java基础之多线程

news2024/11/24 4:34:37

多线程

  • 一、多线程的创建
    • 1.1 方式一 继承Thread类
    • 1.2 方式二 实现Runnable接口
    • 1.3 方式二的化简(匿名内部类)
    • 1.4 实现Callable接口(JDK5新增)
    • 1.5 小节
  • 二、Thread常用API
    • 2.1 获取当前线程对象
    • 2.2 获取/设置线程名称
    • 2.3 Thread的构造器
    • 2.4 Thread类的线程休眠方法
    • 2.5 小节
  • 三、线程安全问题
    • 3.1 问题出现场景
    • 3.2 面向对象模拟出线程安全问题
  • 四、线程同步
    • 4.1 如何保证线程安全
    • 4.2 线程同步的核心思想-加锁
    • 4.3 线程同步方式一:同步代码块
    • 4.4 锁对象规范
    • 4.5 线程同步方式二:同步方法
    • 4.6 同步方法和同步代码块的区别
    • 4.7 线程同步方式三:Lock锁(JDK5)
  • 五、线程池
    • 5.1 线程池的作用
    • 5.2 ThreadPoolExecutor创建线程池
    • 5.3 临时线程什么时候创建
    • 5.4 什么时候执行任务拒绝策略
    • 5.5 线程池处理流程
    • 5.6 线程池处理Runnable任务
    • 5.7 线程池处理Callable任务
    • 5.8 Executors工具类创建线程池
    • 5.9 Executors存在的问题
  • 六、定时器
    • 6.1 Timer实现
    • 6.2 ScheduleExecutorService实现
  • 七、并发并行、线程的生命周期
    • 7.1 并发和并行
    • 7.2 线程的生命周期

一、多线程的创建

1.1 方式一 继承Thread类

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

public class Create {
    //通过继承Thread类
    public static void main(String[] args) {
        //3.new一个新的线程对象
        Thread t1 = new MyThread();//多态
        //4.调用start方法启动线程(最终执行的还是run方法) 思考:为什么不直接调用run方法?
        t1.start();//主线程从进入main方法时就启动了 执行到这里 主线程把t1线程启动了

        //5.主线程(main)里执行的任务
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }

    }
}

//1.定义一个线程类(这只是一个类 并不是线程对象) 继承Thread类
class MyThread extends Thread {

    //2.重写Thread的run方法 定义这个线程以后要干啥
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程执行中" + i);
        }
    }
}

为什么不直接调用run方法?
在这里插入图片描述

  • 假如直接调用run方法 主线程执行到run的时候 就把它当成一个类的一个方法 不会开启一个新的线程 依然就是主线程往下执行 导致永远都是先跑完run方法 再继续执行主线程
  • 而start方法 就告诉了操作系统 执行到我这 请CPU把它当做一个单独的执行流程 以线程的方式来启动run方法

1.2 方式二 实现Runnable接口

  • 这种实现接口的方式 将来就可以继承其他类 优化了方式一单继承的确定

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

public class Create02 {
    public static void main(String[] args) {
        //3.实例化一个[任务对象]
        Runnable myRunnable = new MyRunnable();//多态

        //4.把一个[任务对象]交给一个[线程对象]来处理
        //调用Thread类的构造器 Thread(Runnable target)
        //new Thread(myRunnable).start();
        Thread t = new Thread(myRunnable);
        t.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }
    }
}


//1.定义一个线程[任务类] 实现Runnable接口
class MyRunnable implements Runnable {

    //2.重写run方法 编写线程要完成的任务
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程执行中" + i);
        }
    }
}

在这里插入图片描述

1.3 方式二的化简(匿名内部类)

public class test01 {
    public static void main(String[] args) {
        //实例化一个[任务对象]
        Runnable myRunnable = new Runnable() {
            //直接new Runnable的匿名对象
            //右边这一坨 就相当于Runnable的一个[实现类对象]
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程执行中" + i);
                }
            }
        };

        Thread t = new Thread(myRunnable);
        t.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }
    }
}

化简一下:

public class test01 {
    public static void main(String[] args) {

        Thread t = new Thread(new Runnable() {
            //new Thread的构造器 传进来一坨Runnable的匿名对象
            //然后这一整托就创建了一个线程对象
            //这个线程对象 可以返回给某个变量  也可以直接作为一个对象来调用方法
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程执行中" + i);
                }
            }
        });

        t.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }
    }
}

进一步化简(匿名对象):

public class test01 {
    public static void main(String[] args) {

       new Thread(new Runnable() {
            //这个线程对象 也可以直接作为一个对象来调用方法
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("子线程执行中" + i);
                }
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }
    }
}

Lambda表达式继续化简

public class test01 {
    public static void main(String[] args) {

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("子线程执行中" + i);
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程执行中" + i);
        }
    }
}

1.4 实现Callable接口(JDK5新增)

  • 为了优化前两种方式 重写的run方法不能返回结果的问题(有些业务场景 可能需要返回线程执行结果)
  • 任务的执行结果 就是call方法执行完的结果
    在这里插入图片描述

主要理解:

  • Callable任务对象交给FutureTask对象 FutureTask对象(属于Runnable接口家族)就可以交给Thread线程对象了
  • FutureTask的特有方法get会等待子线程任务跑完 再去读取它的返回结果
  • 在这里插入图片描述
  • 在这里插入图片描述
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class test02 {
    public static void main(String[] args) {
        //Callable任务对象交给FutureTask对象 FutureTask对象(属于Runnable接口家族)就可以交给Thread线程对象了

        //3.创建Callable[任务对象]callable
        Callable<String> callable = new MyCall(200);

        //可以把callable这个任务 像之前一样直接给线程对象吗?-不可以 所以还需要一层封装-FutureTask
        //Thread t = new Thread(call);//err [Thread构造器只能接Runnable类型的]

        //4.把callable任务对象 交给FutureTask类的对象(FT的构造器能接Callable类型)
        // FutureTask类 [实现]了RunnableFuture接口 RunnableFuture接口又[继承]了Runnable接口
        // 所以FutureTask的对象 本质上是Runnable的一个对象 而Runnable就可以交给Thread了
        // FutureTask的对象通过它的get方法 得到线程的执行结果
        // get方法能追踪call方法 [只有等call方法跑完了(线程执行完毕了)] 才会去拿执行结果
        // 不能用callable对象直接调用call方法 因为这样去调call方法的时候 并不能保证call方法已经跑完了
        FutureTask<String> futureTask = new FutureTask<>(callable);
        //Runnable ft = new FutureTask<>(callable);//多态 但是get是FT的[特有方法] 所以不能这么写

        //5.把futureTask交给Thread线程处理
        Thread t1 = new Thread(futureTask);
        t1.start();

        try {
            //get方法拿到线程执行结果 最终找的就是call方法的返回结果
            //当主线程运行到这 就要去拿子线程的结果了 这个时候call方法可能都没跑完
            //但是别担心 [如果没执行完 这里的代码就会等待 直到线程跑完 才拿结果]
            System.out.println(futureTask.get());
            //所以这里不能直接调call() 它不会监测子线程任务有没有跑完
//            System.out.println(callable.call());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

//1.定义一个任务类 实现Callable[泛型]接口
// 因为Callable里面的任务要返回结果 泛型规定返回结果的类型
class MyCall implements Callable<String> {
    private int n;

    public MyCall(int n) {
        this.n = n;
    }

    //2.重写call方法 线程要完成什么任务
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "子线程执行结果是" + sum;
    }
}

1.5 小节

在这里插入图片描述

二、Thread常用API

2.1 获取当前线程对象

在这里插入图片描述

2.2 获取/设置线程名称

在这里插入图片描述

在这里插入图片描述

2.3 Thread的构造器

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

2.4 Thread类的线程休眠方法

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

2.5 小节

  • 我们调start启动线程之后 run方法是线程自己去调用的
    在这里插入图片描述

三、线程安全问题

3.1 问题出现场景

为什么会出现问题?
在这里插入图片描述

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改资源的操作(读资源是不会出现线程安全问题的)
    在这里插入图片描述

3.2 面向对象模拟出线程安全问题

在这里插入图片描述

在这里插入图片描述


下面是不断执行程序 可能出现的结果
这说明小明和小红两个线程同时去取钱
最后结果到底是谁先去取钱 谁的取钱动作更快 有没有在下一个人来取钱之后把余额更新这些都是不确定的
很容易引发线程安全问题!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、线程同步

4.1 如何保证线程安全

在这里插入图片描述
上面的取钱之所以存在安全问题 就是因为多个线程去取钱发现钱都是够的
虽然ABCD很多人可以同时访问同一个账户去取钱 但是在取钱那一刻 开始排队 就没有问题了

4.2 线程同步的核心思想-加锁

怎么保证线程先后依次访问共享资源?(唯一的锁对象)
在这里插入图片描述
在这里插入图片描述

4.3 线程同步方式一:同步代码块

在这里插入图片描述

在这里插入图片描述

  • 小明小红来取钱 可能有先后 那么就让先来的人解锁进去取钱 然后更新余额
    等先来的人走了 就解锁 第二个人进来发现余额不足
  • 就算是他俩真的在时间维度上都是同一时刻来的 也存在锁竞争的算法 一定也只会让一个人进去取钱
    在这里插入图片描述

4.4 锁对象规范

思考:上面随意地用"唯一"这种字符串作为锁 合不合理?
虽然理论上是可以 但是会影响其他无关线程的执行
假如有另外一家人 小黑小白 来取钱
如果我用的是"唯一"这种锁
那么小黑小白在小明取钱的时候 也被锁在外面等
这显然不合理
应该一家人用一家人的锁才对

  • 建议使用共享资源作为锁对象
  • 对于实例方法 建议用this作为锁对象

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

  • 对于静态方法 建议用字节码对象(类名.class)作为锁对象
    在这里插入图片描述

4.5 线程同步方式二:同步方法

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

在这里插入图片描述

在这里插入图片描述

4.6 同步方法和同步代码块的区别

  • 同步代码块锁的范围更小 性能会好一些
    同步代码块相当于把厕所的坑锁起来了 多个线程可以都同时跑到厕所里面去

  • 同步方法锁的范围更大 性能相对较差(但是可读性好 所以用的也多)
    它相当于把整个厕所锁起来了 一起跑到厕所门口等 像之前的取钱案例 同步代码块还能一起先拿到name 同步方法只能在整个方法外面等着

4.7 线程同步方式三:Lock锁(JDK5)

在这里插入图片描述

小明 小红两个线程(用户) 对同一个账户来操作 拿的锁对象也是同一把
(一个账户对应一把锁)
在这里插入图片描述

在这里插入图片描述

五、线程池

5.1 线程池的作用

  • 线程池就是一个可复用线程的技术 可以提高性能
  • 工作线程也叫核心线程
  • ExecutorService接口代表了线程池
  • 任务队列里的任务 是通过任务接口Runnable和Callable创建的
  • 不使用线程池的话 每次用户提交任务都新建线程 对于内存和CPU的开销都很大(java中 线程就是对象 对象存在内存)
    在这里插入图片描述

5.2 ThreadPoolExecutor创建线程池

  • 通过ExecutorService接口的实现类ThreadPoolExecutor自创一个线程池对象
  • 使用Executors(线程池的工具类)调用API 返回不同特点的线程池对象

在这里插入图片描述
参数三指定的是临时线程的存活时间 也就是除去核心线程(长期工) 剩下的那些(临时工)

  • corePoolSize 核心线程数有新任务提交时会进行下面的判断
    1.如果当前线程数(正在工作的线程)小于corePoolSize,则新建(核心)线程处理任务,即使此时有空闲线程也不使用
    2.如果当前线程数(正在工作的线程)大于corePoolSize且小于maximumPoolSize(说明还能创建临时线程)如果workQueue未满,则加入workQueue,否则新建(临时)线程处理此任务
    3.如果corePoolSize 和 maximumPoolSize相同,当workQueue未满时放入workQueue,否则按照拒绝策略处理新任务
    4.如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务。

  • workQueue
    假设10个最大线程数的线程都在忙 第11个任务提交的时候 如果任务队列没满 就进去这个任务队列缓存起来

  • keepAliveTime
    临时线程空闲时间等待超过这个keepAliveTime时间才会被回收

  • handler 拒绝策略,分为下面几种
    1.AbortPolicy,直接抛出异常,默认的拒绝策略;
    2.CallerRunsPolicy,使用调用者所在的线程执行任务(绕过线程池 主线程亲自来服务);
    3.DiscardOldestPolicy,丢弃队列中最前面的任务(等待最久的任务),并将当前任务加入到队列中;
    4.DiscardPolicy,直接丢弃任务

本段参数说明原文链接

5.3 临时线程什么时候创建

核心线程都在忙;临时线程还能创建;任务队列也满了
这个时候提交新任务 就会创建临时线程(看5.2的核心线程第二点判断)
为什么要这么做?
这样其实尽量少创建了线程
因为核心线程都在忙 任务队列也没排满
核心线程可能马上就忙完了 就可以处理其他任务了
只有等任务队列都满了 还有新任务提交 这种极端情况 才要临时线程登场帮忙
如果核心线程都在忙 新任务来了直接就创建临时线程 资源利用率就低了
在这里插入图片描述

5.4 什么时候执行任务拒绝策略

如果运行的线程数量大于等于maximumPoolSize
这时如果workQueue已经满了
则通过handler所指定的策略来处理任务
在这里插入图片描述

5.5 线程池处理流程

在这里插入图片描述

5.6 线程池处理Runnable任务

import java.util.concurrent.*;
public class test05 {
    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(
                //创建线程池对象
                //三个核心线程池 最大线程数5 任务队列5  也就是最多处理5+5个任务
                3,5,6,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        Runnable task = new MyThread001();
        //到这里 每次都让一个核心线程来工作
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);

        //当我加上sleep 上面三个还没跑完 到这里 这里的任务只会等待(如果任务不超过任务队列能接纳的5个)
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);
        pool.execute(task);

        //再来一个 超过五个了 就要派出临时线程了(并不一定只创建一个)
        //最对5-3=2个临时线程
        pool.execute(task);
        pool.execute(task);

        // 核心线程都在忙
        // 临时线程创建完了 也都在满
        // 任务队列也满了
        // 这个时候要执行拒绝策略了
        pool.execute(task);
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.7 线程池处理Callable任务

  • 使用ExecutorService接口提供的submit方法
  • 同样这里的get方法会等任务跑完再取出结果
    在这里插入图片描述

在这里插入图片描述

5.8 Executors工具类创建线程池

在这里插入图片描述

import java.util.concurrent.*;

public class test05 {
    public static void main(String[] args) throws Exception{

        //创建固定线程数量的线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(3);

        pool.execute(new MyThread001());
        pool.execute(new MyThread001());
        pool.execute(new MyThread001());

        // 这个任务会一直被阻塞 除非run里面没有sleep
        // 那么三个核心线程应该能对付>3个任务
        pool.execute(new MyThread001());
    }
}

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

5.9 Executors存在的问题

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

六、定时器

6.1 Timer实现

  • 定时器是一种控制任务延时调用 周期调用的技术
  • TimerTask实现了Runnable接口
  • 他最大的问题是单线程执行
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
import java.util.Timer;
import java.util.TimerTask;

public class Timer_ {
    public static void main(String[] args) {
        Timer timer = new Timer();//定时器本身就是一个单线程
        //TimerTask task, long delay, long period
        //这一坨代表一个匿名对象(千万不能看成[抽象类]TimerTask的实现类 但可以看成TimerTask的子类的对象)
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行了一个任务");
            }
        },3000,2000);//3s后开始执行 每过2s执行一次
    }
}

Timer存在的问题:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6.2 ScheduleExecutorService实现

  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Timer_ {
    public static void main(String[] args) {
        //1.创建ScheduledExecutorService线程池 做为定时器
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

        //2.开启定时任务1
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
//                System.out.println(10/0); //这个任务就算挂了 也不影响任务2
                try {
                    System.out.println(Thread.currentThread().getName() + "在执行任务" + new Date());
                    Thread.sleep(5000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);//不延迟 2s执行一次

        //3.开启定时任务2 不会收到任务1的影响
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "在执行任务" + new Date());
            }
        }, 0, 1, TimeUnit.SECONDS);//不延迟 2s执行一次
    }
}

七、并发并行、线程的生命周期

7.1 并发和并行

  • 正在运行的程序(软件 跑起来的java代码)就是一个独立的进程,线程是属于进程
  • 线程其实是CPU并发与并行同时执行
  • 并发就是CPU分时轮询执行线程 但是切换的速度贼快
  • 并行是站在时间维度上 任何一个时刻 CPU最多在处理8个线程(8个取决于CPU有几个逻辑处理器)

假设我CPU的逻辑处理器是8 那么每次最多同时处理(并行)8个线程
但是计算机肯定不止处理8个线程
这时候就会并发执行:CPU一会处理这几个线程 一会又处理那几个线程 轮询提供服务 但是切换的速度极快 所以看上去似乎好多线程在同时执行 这就是并发
也就是说我这个8个逻辑处理器的CPU 在任何一个时刻/瞬间 最多都只会处理8个线程(这就是并行)

7.2 线程的生命周期

在这里插入图片描述

  • 新建状态只有Java(对象)特征 没有线程特征
  • 调用sleep()休眠之后 不会释放锁 小红就算在里面睡一天 小明也得在外面等着
  • 调用wait()进入无限等待状态后 会立即释放锁
  • wait(5000) 如果5s内一直没人唤醒我 我自己就醒了
    在这里插入图片描述

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

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

相关文章

3 CUDA硬件概述

3.1 PC 架构 首先&#xff0c;我们看看当下许多PC中都使用的酷睿2(Core2)处理器的典型架构&#xff0c;然后分析一下它是如何影响我们使用GPU 加速器的(如图 3-1所示)。 图3-1典型的酷睿2(Core2)系列处理器的结构图 由于所有的 GPU 设备都是通过 PCI-E(Peripheral Communicat…

【教程】如何自制一个ArcGIS工具箱

ArcGIS已经提供了十分丰富的工具箱&#xff0c;但是如果遇到一些需要批处理或者需要将多个工具箱组合使用&#xff0c;就需要根据需求自制一个ArcGIS的工具箱。 下面介绍一下如何自制一个ArcGIS/ArcGIS Pro工具箱&#xff0c;主要是使用ArcPy和模型构建器。 使用模型构建器 使…

给计算机专业学生的建议

如果条件允许&#xff0c;推荐尝试考研。虽然研究生学历的价值在一定程度上有所下降&#xff0c;但计算机专业研究生的发展前景通常优于本科生。如果决定考研&#xff0c;应尽力提高自己的学校等级。比赛成绩对于求职帮助有限&#xff0c;除非是含金量高的比赛&#xff0c;通常…

隐语笔记1 —— 数据可信流通,从运维信任到技术信任

数据可信流通体系 关于可信的反思 信任是涉及交易或交换关系的基础 信任的基石&#xff1a; 身份可确认利益可依赖能力有预期行为有后果 数据流通中的不可信风险&#xff1a;可信链条失效&崩塌 法规层面&#xff1a;数据的持有权&#xff0c;加工权&#xff0c;经营权…

FPGA学习_时序约束以及VIVADO时序报告

文章目录 前言时序约束的目的一、时序约束种类1、约束主时钟2、约束衍生时钟3、约束虚拟时钟4、input delay5、output delay6、约束异步时钟组7、约束互斥时钟8、假路径约束9、多周期约束 二、VIVADO时序报告三、从时序的角度看为什么寄存器赋值慢一拍 前言 一边学习一边补充当…

Unity Toggle与Toggle Group的妙用

Toggle与Toggle Group结合使用&#xff0c;妙处多多。 因为在同一Toggle Group内只有一个Toggle可以被选中&#xff0c;那么对于我们要创建单选按钮组、游戏的一些开关、暗夜模式、筛选不同显示内容等功能都非常好用。 比如我要实现通过点击不同按钮,从而筛选显示不同内容&am…

IO扩展芯片应用及方案选型 (74HC595,74HC165,8255,CH351等)

IO扩展芯片应用及方案选型 (74HC595,74HC165,8255,CH351等) 参考书籍《振南技术干货集&#xff1a;单片机–基础进阶创业十年》作者&#xff1a;于振南 在我们进行单片机开发的时候, 经常会发现I/O 口不够用。 一方面是因为我们产品中往往都包括很多的功能, 又有显示, 又有存储…

uniapp,导航栏(切换项)有多项,溢出采取左滑右滑的形式展示

一、实现效果 当有多项的导航&#xff0c;或者说切换项&#xff0c;超出页面的宽度&#xff0c;我们采取可滑动的方式比较好一些&#xff01;并且在页面右边加个遮罩&#xff0c;模拟最右边有渐变效果&#xff01; 二、实现代码 html代码&#xff1a; <!-- 头部导航栏 --…

探究Kafka主题删除失败的根本原因

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 探究Kafka主题删除失败的根本原因 前言主题删除的基础主题删除的定义和作用&#xff1a;删除操作的基本流程&#xff1a; 可能存在删除异常的因素数据积压的处理方法Broker状态异常处理方法通用方法 前…

PyTorch 深度学习(GPT 重译)(一)

第一部分&#xff1a;PyTorch 核心 欢迎来到本书的第一部分。在这里&#xff0c;我们将与 PyTorch 迈出第一步&#xff0c;获得理解其结构和解决 PyTorch 项目机制所需的基本技能。 在第一章中&#xff0c;我们将首次接触 PyTorch&#xff0c;了解它是什么&#xff0c;解决了…

【JS】数组查重

码 /*** 数组查重* param {Array} arr 查重的数组* returns Array 返回不重复的数组 */ const noReArr arr > [...new Set(arr)]const a [12,12,3,4] console.log( noReArr(a) );效果图

使用STM32 再实现电动车防盗

项目需求 点击遥控器 A 按键&#xff0c;系统进入警戒模式&#xff0c;一旦检测到震动&#xff08;小偷偷车&#xff09;&#xff0c;则喇叭发出声响报警&#xff0c; 吓退小偷。 点击遥控器 B 按键&#xff0c;系统退出警戒模式&#xff0c;再怎么摇晃系统都不会报警&…

vscode搜索总是搜到修改记录文件如何处理

如图每次搜索出来的结果总是有好多编辑记录中的文件&#xff0c;给自己找文件及提交代码时带来很大困扰&#xff0c;每次都得删特别麻烦。 解决办法&#xff1a; 如上图在插件里找到 local history 点击 ”禁用“ 即可。以后再编辑代码就不会产生修改记录文件了。 如果直接搜索…

【matlab安装casadi】

虽然安装起来很简单&#xff0c;但是网上没找到好的教程&#xff0c;姑且写一下记录一下 首先到github找到对应的库&#xff1a;https://github.com/casadi/casadi找到发布的版本&#xff0c;点进去 这里就可以点进去下载自己需要的版本了下面也有对应的下载后的安装方式&…

ArcGIS Pro与R:携手优化生态系统服务评估流程

生态系统服务是指生态系统所形成的用于维持人类赖以生存和发展的自然环境条件与效用&#xff0c;是人类直接或间接从生态系统中得到的各种惠益。联合国千年生态系统评估&#xff08;Millennium ecosystem assessment&#xff0c;MA&#xff09;提出生态系统服务包括供给、调节、…

linux系统--------------mysql数据库管理

目录 一、SQL语句 1.1SQL语言分类 1.2查看数据库信息 1.3登录到你想登录的库 1.4查看数据库中的表信息 1.5显示数据表的结构&#xff08;字段&#xff09; 1.5.1数据表的结构 1.5.2常用的数据类型: 二、关系型数据库的四种语言 2.1DDL&#xff1a;数据定义语言&am…

【堆】Top-K问题

标题&#xff1a;C语言库函数scanf&#xff08;&#xff09;解读 水墨不写bug &#xff08;图片来源于网络&#xff09; 正文开始&#xff1a; Top-K问题是一类问题的统称&#xff1a; 即根据对象的某一属性&#xff0c;找出这个属性最突出的K个对象&#xff0c;并且通常对象…

Midjourney发布新特性风格参考

1. 引言 最近&#xff0c;Midjourney 推出了Style Reference V2.0 即功能更加强大的风格参考工具&#xff0c;该工具可以让大家参考其他图像的风格&#xff0c;生成与参考图像风格保持一致&#xff0c;与文本提示词语义内容保持一致的图像。它与图像提示类似&#xff0c;但是只…

LightDB24.1 存储过程支持inner和outer对变量的引用

背景 Oracle oracle plsql支持如下场景&#xff1a; 在for循环中&#xff0c;将select查询的结果给一个record类型&#xff0c;这一操作也被称为隐式游标操作。record类型中一个字段用来接收查询结果中的一个select查询语句&#xff08;update,delete,insert在这个语法中都会…

ELK快速搭建图文详细步骤

目录 一、下载地址二、安装docker-compose(已安装则跳过)三、初始化ELK1. 赋予/setup/entrypoint.sh执行权限2. 初始化 docker-elk 所需的 Elasticsearch 用户和组3. 重置默认用户的密码4. 替换配置文件中的用户名和密码5. 重启 Logstash 和 Kibana&#xff0c;使用新密码重新连…