【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【15】异步_线程池

news2024/11/22 18:16:04

持续学习&持续更新中…

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【15】异步_线程池

  • 初始化线程的 4 种方式
  • 开发中为什么使用线程池
  • 线程池七大参数
  • 线程池工作原理
  • 常见的 4 种线程池
  • 生产中如何使用线程池?
  • CompletableFuture 异步编排—简介
    • 业务场景
    • 简介
  • CompletableFuture—创建(run/supply)
  • CompletableFuture—计算完成(whenComplete)
  • CompletableFuture—处理(handle)
  • CompletableFuture—线程串行化方法
  • CompletableFuture—两任务组合—都要完成(Both/Combine)
  • CompletableFuture—两任务组合—一个完成(Either)
  • CompletableFuture—多任务组合
  • 参考

初始化线程的 4 种方式

1)、继承 Thread
2)、实现 Runnable 接口
3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
4)、线程池

  • 方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景,也会导致资源耗尽

  • 方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。极大可能导致服务器资源耗尽。

  • 我们以后在业务代码里面,方式123,这三种启动线程的方式都不用。可能会导致资源耗尽【应该将所有的多线程异步任务都交给线程池执行】

  • 方式 4:通过如下两种方式初始化线程池(当前系统中线程池最好只有一两个,每个异步任务,提交给线程池让他自己去执行就行)

    • 1、Executors.newFiexedThreadPool(3);

    • 2、new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);

开发中为什么使用线程池

  • 降低资源的消耗:通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗

  • 提高响应速度:因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于 等待分配任务 的状态,当任务来时无需创建新的线程就能执行

  • 提高线程的可管理性:线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

    public static void main(String[] args)  throws ExecutionException, InterruptedException {
        System.out.println("main....start....");
        /**
         * 1)、继承Thread
         *         Thread01 thread = new Thread01();
         *         thread.start();//启动线程
         *
         * 2)、实现Runnable接口
         *         Runable01 runable01 = new Runable01();
         *         new Thread(runable01).start();
         * 3)、实现Callable接口 + FutureTask (可以拿到返回结果,可以处理异常)
         *         FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
         *         new Thread(futureTask).start();
         *         //阻塞等待整个线程执行完成,获取返回结果
         *         Integer integer = futureTask.get();
         * 4)、线程池[ExecutorService]
         *         给线程池直接提交任务。
         *         service.execute(new Runable01());
         *     创建:
         *            1)、Executors
         *            2)、new ThreadPoolExecutor
         *
         *      Future:可以获取到异步结果
         *
         * 区别;
         *      1、2不能得到返回值。3可以获取返回值
         *      1、2、3都不能控制资源
         *      4可以控制资源,性能稳定。
         */

//        new Thread(()-> System.out.println("hello")).start();
        //我们以后再业务代码里面,以上三种启动线程的方式都不用。可能会导致资源耗尽【应该将所有的多线程异步任务都交给线程池执行】
//        executor.execute(new Runable01());
//        Future<?> submit = executor.submit(new Thread01());
//        Object o = submit.get();
//        System.out.println(o);
        //当前系统中线程池最好只有一两个,每个异步任务,提交给线程池让他自己去执行就行

        /**
         * 七大参数
         * corePoolSize:核心线程数[一直存在除非设置allowCoreThreadTimeOut]; 线程池创建好以后就会准备就绪这些数量的线程,它们等待接受异步任务去执行。
         *        5个  Thread thread = new Thread();  thread.start();
         * maximumPoolSize:[200] 最大线程数量;  控制资源
         * keepAliveTime:存活时间。如果当前的线程池中的线程数量大于core线程数量。
         *                          并且只要线程空闲时间大于指定的keepAliveTime,也就是线程在最大多长时间没有接到新任务
         *                          就会释放(maximumPoolSize-corePoolSize)数量空闲的线程,最终线程池维持在 corePoolSize大小

         *
         * unit:时间单位
         * BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面。
         *              只要有线程空闲,就会去队列里面取出新的任务继续执行。
         * threadFactory:线程的创建工厂。
         * RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略拒绝执行任务
         *
         *
         *
         * 工作顺序:
         * 1)、线程池创建,准备好core数量的核心线程,准备接受任务
         * 1.1、core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行
         * 1.2、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
         * 1.3、max满了就用RejectedExecutionHandler拒绝任务
         * 1.4、max都执行完成,有很多空闲.在指定的时间keepAliveTime以后,释放max-core这些线程
         *
         *      new LinkedBlockingDeque<>():默认是Integer的最大值。内存不够
         *
         * 一个线程池 core 7; max 20 ,queue:50,100并发进来怎么分配的;
         * 7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略。
         * 如果不想抛弃还要执行。CallerRunsPolicy;
         *
         */
        ExecutorService executor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
//        Executors.newCachedThreadPool() core是0,所有都可回收
//        Executors.newFixedThreadPool() 固定大小,core=max;都不可回收
//        Executors.newScheduledThreadPool() 定时任务的线程池
//        Executors.newSingleThreadExecutor() 单线程的线程池,后台从队列里面获取任务,挨个执行
        //
        System.out.println("main....end....");
    }

线程池七大参数

在这里插入图片描述

在这里插入图片描述

corePoolSize:

  • the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
  • 核心线程数[一直存在除非设置allowCoreThreadTimeOut];
  • 线程池创建好以后就会将这些数量的线程准备就绪,它们等待接受异步任务去执行。

maximumPoolSize:

  • the maximum number of threads to allow in the pool
  • 最大线程数量;
  • 可以控制资源

keepAliveTime:

  • when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
  • 存活时间。
  • 如果当前的线程池中的线程数量大于core线程数量。
  • 并且只要这些多余的空闲线程空闲时间大于指定的keepAliveTime,也就是线程在最大多长时间没有接到新任务
  • 就会释放(maximumPoolSize-corePoolSize)数量空闲的线程,最终使得线程池维持在 corePoolSize 大小

unit:

  • the time unit for the keepAliveTime argument
  • 时间单位

workQueue(阻塞队列)

  • the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
  • 如果任务有很多,就会将目前多的任务放在队列里面。
  • 队列用来存储等待执行的任务,只要有线程空闲,就会去队列里面取出新的任务继续执行。

threadFactory:

  • the factory to use when the executor creates a new thread
  • 线程的创建工厂。

handler:(RejectedExecutionHandler)

  • the handler to use when execution is blocked because the thread bounds and queue capacities are reached
  • 如果队列满了,按照我们指定的拒绝策略拒绝执行任务:
    • AbortPolicy【默认】:直接拒绝策略,也就是不会执行任务,直接抛出RejectedExecutionException,这是默认的拒绝策略。
    • DiscardPolicy:抛弃策略,也就是直接忽略提交的任务(通俗来说就是空实现)。
    • DiscardOldestPolicy:抛弃最老任务策略,也就是通过poll()方法取出任务队列队头的任务抛弃,然后执行当前提交的任务。
    • CallerRunsPolicy:调用者执行策略,也就是当前调用Executor#execute()的线程直接调用任务Runnable.run(),一般不希望任务丢失会选用这种策略,但从实际角度来看,原来的异步调用意图会退化为同步调用。

线程池工作原理

在这里插入图片描述

在这里插入图片描述

文字描述:

  • 在创建了线程池后,开始等待请求。

  • 当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

    • 如果正在运行的线程数量小于corePoolSize,那么马上创建核心线程运行这个任务;
    • 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
    • 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    • 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行。

  • 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

    • 如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
    • 所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

常见的 4 种线程池

在这里插入图片描述

在这里插入图片描述

点进源码可以去看一看:

Executors.newCachedThreadPool() core是0,所有都可回收
Executors.newFixedThreadPool() 固定大小,core=max;都不可回收
Executors.newScheduledThreadPool() 定时任务的线程池
Executors.newSingleThreadExecutor() 单线程的线程池,后台从队列里面获取任务,挨个执行

生产中如何使用线程池?

在工作中 单一的/固定数的/可变的 三种创建线程池的方法哪个用的多?

答案是一个都不用,我们工作中只能使用自定义的

Executors中JDK已经给你提供了,为什么不用?

在这里插入图片描述

一般不要把最大线程数写死:

    final int availableProcessors = Runtime.getRuntime().availableProcessors();
    int maximumPoolSize = availableProcessors + 1;
    ExecutorService threadPool = new ThreadPoolExecutor(
            2,
            maximumPoolSize,
            200L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(3),
            Executors.defaultThreadFactory(),
		    new ThreadPoolExecutor.AbortPolicy()
		//   new ThreadPoolExecutor.CallerRunsPolicy()
		//   new ThreadPoolExecutor.DiscardPolicy()
        //    new ThreadPoolExecutor.DiscardOldestPolicy()
    );

CompletableFuture 异步编排—简介

业务场景

通过线程池性能稳定,也可以获取执行结果,并捕获异常。

但是,在业务复杂情况下,一 个异步调用可能会依赖于另一个异步调用的执行结果。

业务场景: 查询商品详情页的逻辑比较复杂(第4/5/6步需要获取到第1步的数据才能去查询),有些数据还需要远程调用,必然需要花费更多的时间。

在这里插入图片描述

假如商品详情页的每个查询,需要以上标注的时间之和才能完成,那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s(某个最大耗时) 即可完成响应。

简介

  • Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计 算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel 方法停止任务的执行。

  • 虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不 方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的 初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为 什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

  • 很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自 己扩展了 Java 的 Future接口,提供了addListener等多个扩展方法;Google guava 也提供了 通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。
    作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?

  • 在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的 Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以 通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。

  • CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或 者轮询的方式获得结果,但是这种方式不推荐使用。

  • CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。

在这里插入图片描述

CompletableFuture—创建(run/supply)

在这里插入图片描述

  • runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的

  • 可以传入自定义的线程池,否则就用默认的线程池;

CompletableFuture—计算完成(whenComplete)

在这里插入图片描述

  • whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。

  • whenComplete 和 whenCompleteAsync 的区别:

    • whenComplete:是执行当前任务的线程(创建该CompletableFuture的线程)继续执行 whenComplete的任务。
    • whenCompleteAsync:是把 whenCompleteAsync 这个任务继续提交给线程池来执行。
  • 方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程 执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getId());
    int i = 10 / 0;
    System.out.println("运行结果:" + i);
    return i;
}, executor).whenComplete((res,excption)->{
    //虽然能得到异常信息,但是没法修改返回数据。
    System.out.println("异步任务成功完成了...结果是:"+res+";异常是:"+excption);
}).exceptionally(throwable -> {
    //可以感知异常,同时返回默认值
    return 10;
});
    public static void main(String[] args) throws Exception {
        System.out.println(Thread.currentThread().getName());


        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\tfuture");
            return 1024;
        });
        completableFuture
                .whenComplete((t, u) -> {
                    System.out.println(Thread.currentThread().getName() + "\twhenComplete");
                })
                .exceptionally(f -> {
                    return 4444;
                }).get();
        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName());






        new Thread(() -> {
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\tfuture");
                return 1024;
            });
            try {
                future
                        .whenComplete((t, u) -> {
                            System.out.println(Thread.currentThread().getName() + "\twhenComplete");
                        })
                        .exceptionally(f -> {
                            return 4444;
                        }).get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "线程a").start();
//
        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName());






        new Thread(() -> {
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "\tfuture");
                return 1024;
            });
            try {
                future
                        .whenCompleteAsync((t, u) -> {
                            System.out.println(Thread.currentThread().getName() + "\twhenCompleteAsync");
                        })
                        .exceptionally(f -> {
                            return 4444;
                        }).get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "线程b").start();
    }

CompletableFuture—处理(handle)

在这里插入图片描述

和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

        /**
         * 方法执行完成后的处理【无论成功完成还是失败完成】
         */
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 4;
            i = i / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, executor)
                .handle(
                            (res, thr) -> {
                                if (res != null) {
                                    return res * 2;
                                }
                                if (thr != null) {
                                    return 0;
                                }
                                return 0;
                            }
                       );
//        R apply(T t, U u);
        System.out.println(future.get());
        Thread.sleep(50000);
        if(1==1) throw new RuntimeException();

CompletableFuture—线程串行化方法

在这里插入图片描述

  • 带有 Async 默认是异步执行的。同之前。

  • thenRun方法:【不接收,不返回】只要上面的任务执行完成,就开始执行thenRun。

  • thenAccept方法:【只接收,无返回】能接收消费上一步处理的结果,无返回结果。

  • thenApply方法:【既接收,又返回】当一个线程依赖另一个线程时,获取上一个任务返回的结果,并且当前任务也有返回值可以传递给下一个异步任务。

/**
 * 线程串行化
 * 1)、thenRun:不能获取到上一步的执行结果,无返回值
 *  .thenRunAsync(() -> {
 *             System.out.println("任务2启动了...");
 *         }, executor);
 * 2)、thenAcceptAsync;能接受上一步结果,但是无返回值
 * 3)、thenApplyAsync:;能接受上一步结果,有返回值
 */
 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
     System.out.println("当前线程:" + Thread.currentThread().getId());
     int i = 10 / 4;
     System.out.println("运行结果:" + i);
     return i;
 }, executor).thenApplyAsync(res -> {
     System.out.println("任务2启动了..." + res);
     return "Hello " + res;
 }, executor);
 
 future.get() //阻塞方法

CompletableFuture—两任务组合—都要完成(Both/Combine)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 两个任务必须都完成,触发该任务。

  • thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值

  • thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有 返回值。

  • runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后, 处理该任务。

        /**
         * 两个都完成
         */
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程:" + Thread.currentThread().getId());
            int i = 10 / 4;
            System.out.println("任务1结束:");
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程:" + Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
                System.out.println("任务2结束:");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello";
        }, executor);

//        future01.runAfterBothAsync(future02, () -> {
//            System.out.println("任务3开始...runAfterBothAsync");
//        }, executor);
//
//        future01.thenAcceptBothAsync(future02, (f1, f2) -> {
//            System.out.println("任务3开始...之前的结果thenAcceptBothAsync:" + f1 + "--》" + f2);
//        }, executor);

        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
            return f1 + "  :" + f2 + " -> Haha       thenCombineAsync";
        }, executor);

        System.out.println(future.get());

CompletableFuture—两任务组合—一个完成(Either)

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

  • 当两个任务中,任意一个 future 任务完成的时候,执行任务。

  • applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。

  • acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。

  • runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返 回值。

        /**
         * 两个任务,只要有一个完成,我们就执行任务3
         * runAfterEitherAsync:不感知结果,自己没有返回值
         * acceptEitherAsync:感知结果,自己没有返回值
         * applyToEitherAsync:感知结果,自己有返回值
         */
//        future01.runAfterEitherAsync(future02, () -> {
//            System.out.println("任务3开始...没有之前的结果");
//        }, executor);
//        future01.acceptEitherAsync(future02, (res) -> {
//            System.out.println("任务3开始...之前的结果:" + res);
//        }, executor);
        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
            System.out.println("任务3开始...之前的结果:" + res);
            return res.toString() + "->哈哈";
        }, executor);
        System.out.println(future.get());

CompletableFuture—多任务组合

在这里插入图片描述

  • allOf:等待所有任务完成

  • anyOf:只要有一个任务完成

        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息" + Thread.currentThread().getName());
            return "hello.jpg";
        }, executor);

        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("查询商品的属性");
            return "黑色+256G";
        }, executor);

        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("查询商品介绍");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "华为";
        }, executor);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        allOf.get();//等待所有结果完成
        System.out.println("main....end....");
        System.out.println(futureImg.get() + "=>" + futureAttr.get() + "=>" + futureDesc.get()); //直接拿来future的结果来用


//        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
//        anyOf.get();//等待任意一个结果完成
//        System.out.println("main....end....");
//        System.out.println(anyOf.get());

参考

雷丰阳: Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目.

Throwable: 硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的实现原理.

话唠扇贝: 线程池 ThreadPoolExecutor 详解.


本文完,感谢您的关注支持!


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

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

相关文章

dataX同步SQLserver到MySQL数据

引用datax官方描述&#xff1a; DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS…

如何利用“AI交互数字人+展厅”拓展文娱消费空间?

打造新生代潮玩聚集地&#xff0c;打造演艺新空间&#xff0c;促进虚拟现实体验等文娱业态场景创新&#xff0c;成为了当下发展文旅消费新场景的一大重要手段。数字人汇集了虚拟现实、增强现实、全息投影、人工智能、实时传输语音合成等数字技术&#xff0c;可以利用数字人重构…

在等保2.0的合规性审查中,常见的难点和误区有哪些?

在等保2.0&#xff08;即《信息安全技术 网络安全等级保护基本要求》GB/T 22239-2019&#xff09;的合规性审查中&#xff0c;企业和机构经常会遇到一些难点和误区&#xff0c;主要包括以下几个方面&#xff1a; 常见误区 1. “三同步”不同步&#xff1a;等保2.0强调“同步规…

golang生成RSA公钥和密钥

目录 场景 场景一&#xff1a;加密、解密 场景二&#xff1a;微信退款 场景三&#xff1a;SSL证书 为什么是.key和.pem格式的文件 生成密钥、公钥 密钥、公钥保存到文件中 第一个&#xff1a;保存密钥到文件里 第二个&#xff1a;保存公钥到文件里 场景 场景一&#…

AI产品活跃用户排名出炉!文心一言APP国内第一,Kimi疯狂掉队

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 6月28日&#xff0c;极光大数据旗下月狐数据发布2024年5月中国生成式AI行业市场热点月度分析报告&#xff0c;披露了国内移动端主流生成式AI应用…

一个AI图片生成工具导航网站

上周末上线了一个AI图片生成工具导航网站&#xff0c;主要是面向AI图片工具这个垂直领域。 https://chatgpt-image-generator.com/ 目标是通过收集当下的一些工具&#xff0c;然后进行分类管理&#xff0c;一方面方便大家发现新的工具&#xff0c;另一方面能够更加有针对性、…

操作系统期末复习(对抽象概念的简单形象化)

操作系统 引论 定义与基本概念&#xff1a;操作系统是计算机硬件与用户之间的桥梁&#xff0c;类似于家中的管家&#xff0c;它管理硬件资源&#xff08;如CPU、内存、硬盘&#xff09;&#xff0c;并为用户提供方便的服务&#xff08;应用程序执行、文件管理等&#xff09;。…

Typora 2024 安装教程

本章教程&#xff0c;介绍一下如何使用Typora 最新版本1.9.4&#xff0c;仅供学习交流&#xff0c;切勿滥用。 一、下载安装包 下载地址&#xff1a;https://www.alipan.com/s/8pvKf5ns6GH 当然&#xff0c;你也可以去官网下载&#xff0c;但是官网有可能随时更新&#xff0c;该…

探索CSS布局:创建一个居中的内嵌方块示例

在网页设计中&#xff0c;布局是至关重要的部分。CSS提供了多种方式来实现元素的布局&#xff0c;包括居中对齐、外边距、内边距等。本文将通过一个简单的示例&#xff0c;介绍如何使用CSS来创建一个居中的内嵌方块&#xff0c;并探讨其背后的布局原理。 HTML 结构 首先&…

【Matlab】-- 飞蛾扑火优化算法

文章目录 文章目录 01 飞蛾扑火算法介绍02 飞蛾扑火算法伪代码03 基于Matlab的部分飞蛾扑火MFO算法04 参考文献 01 飞蛾扑火算法介绍 飞蛾扑火算法&#xff08;Moth-Flame Optimization&#xff0c;MFO&#xff09;是一种基于自然界飞蛾行为的群体智能优化算法。该算法由 Sey…

上市公司绿色投资者原始数据+计算代码(2008-2022年)

数据简介&#xff1a;“绿色”信号&#xff0c;意味着潜在环境风险更低&#xff0c;从而绿色投资者降低了对绿色债券的风险补偿要求&#xff0c;推动了信用利差的收窄。因此&#xff0c;绿色投资者会通过投资者风险意识影响债券信用风险。绿色投资者在推动企业绿色可持续发展方…

江山欧派杯2024全国华佗五禽戏线上线下观摩交流比赛在亳州开幕

6月28日&#xff0c;2024全国华佗五禽戏线上线下观摩交流比赛在安徽省亳州市开幕。 此次比赛是由安徽省亳州市文化旅游体育局和安徽省非物质文化遗产保护中心主办、亳州市华佗五禽戏协会&#xff08;国家级非遗华佗五禽戏保护单位&#xff09;和亳州市传统华佗五禽戏俱乐部&…

GIT版本管理工具轻松入门 | TortoiseGit

目录 一、下载git 二、下载tortoisegit&#xff08;可视化git&#xff09; 三、Git本地仓库创建 四、git克隆 五、添加&#xff0c;提交&#xff0c;推送&#xff0c;拉取 六、分支 七、冲突 八、忽略文件&#xff08;修改gitignore文件&#xff09; 一、下载git 安装…

关于转BigDecimal对象时,精度问题

//浮点型数值Double d 0.0003d;//转BigDecimal对象BigDecimal a new BigDecimal(d);System.out.println(String.format("浮点类型数字:%.4f创建BigDecimal对象并且保留多位小数并且保留多位小数时,精度会变多,结果为%s",d,a.setScale(8, BigDecimal.ROUND_DOWN)));…

高电压技术-冲击高压发生器MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 冲击电压发生器是产生冲击电压波的装置&#xff0c;用于检验电力设备耐受大气过电压和操作过电压的绝缘性能&#xff0c;冲击电压发生器能产生标准雷电冲击电压波形&#xff0c;雷电冲击电压截波,标准操作冲击…

Spring底层原理之bean的加载方式三 用注解声明配置类 以及@Configuration 和 @Component 的区别

bean的加载方式三 用注解声明配置类 我们之前用组件扫描加上注解定义bean 实现了bean的加载 当我们又会发现这个配置文件过于繁琐 我们可以写一个类 不是配置文件而是配置类 我们接下来只需要把这句话的功能写到 配置类里面 这样书写就行 package com.bigdata1421.config;…

Git(涵盖GitHub\Gitee码云\GitLab)

Git(涵盖GitHub\Gitee码云\GitLab) 文章目录 Git(涵盖GitHub\Gitee码云\GitLab)课程介绍Git概述官网介绍版本控制介绍两种版本控制工具集中式版本控制工具分布式版本控制工具 Git工作机制代码托管中心 Git安装和客户端的使用Git常用命令设置用户签名初始化本地库查看本地库状态…

软件构造 | Design Patterns for Reuse and Maintainability

Design Patterns for Reuse and Maintainability &#xff08;面向可复用性和可维护性的设计模式&#xff09; Open-Closed Principle (OCP) ——对扩展的开放&#xff0c;对修改已有代码的封 Why reusable design patterns A design… …enables flexibility to change …

uni-appx使用form表单页面初始化报错

因为UniFormSubmitEvent的类型时 e-->detail-->value,然后没有了具体值。所以页面初始化的时候 不能直接从value取值&#xff0c;会报错找不到 所以form表单里的数据我们要设置成一个对象来存放 这个问题的关键在于第22行代码 取值&#xff1a; 不能按照点的方式取值 …

如何在Java中使用Levenshtein距离实现字符串相似度匹配

在许多应用中&#xff0c;我们需要根据用户输入的问题找到最匹配的已知问题。Levenshtein距离&#xff08;编辑距离&#xff09;是一个强大的工具&#xff0c;可以帮助我们衡量两个字符串之间的差异&#xff0c;并进一步计算它们的相似度。本文将使用一个具体的例子来展示如何在…