秒懂SpringBoot之@Async如何自定义线程池

2023-05-16

[版权申明] 非商业目的注明出处可自由转载
出自:shusheng007

文章目录

  • 概述
  • 异步初探
    • 线程池
      • ThreadPoolExecutor
      • ThreadPoolTaskExecutor
    • 验证线程池配置
      • 拒绝策略为AbortPolicy
      • 拒绝策略为DiscardOldestPolicy
      • 拒绝策略为CallerRunsPolicy
      • 拒绝策略为DiscardPolicy
    • @Async 使用
  • 总结
  • 源码

概述

每个Java程序员都有一颗搞高并发的心,所以线程池几乎也是面试必考题。讲线程池的文章网上也特别多特别好,所以本文只是聊一下如何在SpringBoot中使用线程池。

异步初探

在SpringBoot中简单使用异步编程非常简单,只需要两步

  1. 使用@EnableAsync开启异步支持
@EnableAsync
@Configuration
public class ConcurrencyConfig {
...
}
  1. 使用@Async注解相关方法
@Async
public void runAsync(Integer id){
...
}

注意,使用@Async标记的方法必须是public的,而且返回值必须是void或者Future

so easy,有没有不?面试要是这么回答差不多也该回家等消息了。对于稍微有些并发并发量的服务就需要自定义线程池,而不使用Spring默认的SimpleAsyncTaskExecutor,因为其不够灵活。

线程池

线程池相对来说还是比较复杂的,下面是其类图。其中以ThreadPoolExecutor最为重要,面试也基本考这个。

在这里插入图片描述

ThreadPoolExecutor

既然是线程池就会存在各种配置,下面是ThreadPoolExecutor最复查的一个构造函数

 /**
  * Creates a new {@code ThreadPoolExecutor} with the given initial
  * parameters.
  *
  * @param corePoolSize the number of threads to keep in the pool, even
  *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
  * @param maximumPoolSize the maximum number of threads to allow in the
  *        pool
  * @param 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.
  * @param unit the time unit for the {@code keepAliveTime} argument
  * @param workQueue the queue to use for holding tasks before they are
  *        executed.  This queue will hold only the {@code Runnable}
  *        tasks submitted by the {@code execute} method.
  * @param threadFactory the factory to use when the executor
  *        creates a new thread
  * @param handler the handler to use when execution is blocked
  *        because the thread bounds and queue capacities are reached
  * @throws IllegalArgumentException if one of the following holds:<br>
  *         {@code corePoolSize < 0}<br>
  *         {@code keepAliveTime < 0}<br>
  *         {@code maximumPoolSize <= 0}<br>
  *         {@code maximumPoolSize < corePoolSize}
  * @throws NullPointerException if {@code workQueue}
  *         or {@code threadFactory} or {@code handler} is null
  */
 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {
...
 }

我们一般会使用下面这个重载版本。

 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           RejectedExecutionHandler handler) {
     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
          Executors.defaultThreadFactory(), handler);
 }

对于每个参数的含义注释已经写的很清楚了,只是没有实践过的话理解可能不到位。所以一会我们会结合实际来看一下,下面我们简单的解释一下这些配置参数。

  • corePoolSize

线程池中核心线程数目,会一直驻留在线程池中(除非设置allowCoreThreadTimeOut为true,默认为false)。

  • maximumPoolSize

整个线程池允许创建的最大线程数,这个数目包含核心线程数。例如其设置为5,corePoolSize设置为3,那么最多可以再创建2个线程。

  • workQueue:

当新任务到来时,如果没有闲着的核心线程,任务首先会被存放在队列中。

  • keepAliveTime:

那些闲着的非核心线程的存活时间

  • unit

keepAliveTime 参数的时间单位。

  • handler

饱和策略,当线程池没有能力再接收新任务时的处理策略,平台为我们预定义了4种

AbortPolicy:直接抛RejectedExecutionException异常,告知程序线程池已经满负荷了,无法接收新任务

CallerRunsPolicy:让调用线程池的那个线程执行新任务。其实就是因为线程池满负荷了没法执行,它自己把任务执行了。

DiscardOldestPolicy:将任务队列队首第一个任务给丢弃掉,腾出个位置给新任务。

DiscardPolicy:默默的把新任务扔了,连个水花都没有…

从上面的解释我们可以得出,一个线程池最大负荷为 maximumPoolSize + workQueue 个任务。

前三个参数最为重要,配置的时候需要一定的考量,需要根据自己的业务和执行环境来调节。下面是广为流传的配置线程池最大线程个数的一个公式,但是这个只做参考,具体还是要根据自己的实际情况来调节

  • CPU 密集型任务(N+1)

系统大部分时间都在占用CPU 资源,例如内存排序,计算公式等工作,可以将最大线程数设置为 CPU 核心数+1。比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。

  • I/O 密集型任务(2N)

系统大部分时间都在处理I/O交互,例如读取网络文件等工作,而此时是不占用CPU处理时间的。所以我们将线程池的最大线程配置为CUP核数的2倍

ThreadPoolTaskExecutor

上面我们了解了一ThreadPoolExecutor,它是java提供的类。Spring提供了一个它的包装类ThreadPoolTaskExecutor,使得其更容易在spring中使用。我们在Spring程序中一般使用这个类,各个参数含义与ThreadPoolExecutor几乎一样。

了解了线程池的一些概念,让我们来完成配置自定义线程池的任务吧。

  1. 在配置文件中申明一个TaskExecutor类型的Bean
@EnableAsync
@Configuration
public class ConcurrencyConfig {
    @Bean
    public TaskExecutor threadPoolExecutorCpu(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(2);
        executor.setKeepAliveSeconds(1);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadNamePrefix("task-thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        
        executor.initialize();
        return executor;
    }
}

其中大部分参数我们已经在上面讲过了,我这里设置核心线程数目为2,最大线程数目为3,任务队列容量为2,非核心线程闲暇时存活时间为1秒,线程前缀为"task-thread-",无法接收新任务时的策略为AbortPolicy。

  1. 将线程池配置给@Async ,如果只有一个线程池的话是可选的
@Async("threadPoolExecutorCpu")
public void runAsync(Integer id){
    log.info("start:{},num:{}",Thread.currentThread().getId(),id);
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    log.info("end:{},num:{}",Thread.currentThread().getId(),id);
}

验证线程池配置

我们来实际验证一下,加深印象。

下面是一个controller方法,使用postman调用时传入一个count参数来可以产生count个线程调用,模拟并发,每个线程启动时间隔200毫秒,这样线程就有了顺序。

 @GetMapping("/run-async")
 public String runAsync(@RequestParam("count") Integer count) {
     List<Integer> collect = IntStream.rangeClosed(1, count).boxed().collect(Collectors.toList());

     for (int i : collect) {
         new Thread(() -> concurrencyService.runAsync(i)).start();
         try {
             Thread.sleep(200);
         } catch (InterruptedException e) {
             log.error("error", e);
         }
     }
     return "ok";
 }

拒绝策略为AbortPolicy

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
  • 5个并发输出:
2023-03-07 16:59:36.944  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:1
2023-03-07 16:59:37.144  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:2
2023-03-07 16:59:37.819  INFO 17512 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : start:48,num:5
2023-03-07 16:59:39.975  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:1
2023-03-07 16:59:39.976  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:3
2023-03-07 16:59:40.158  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:2
2023-03-07 16:59:40.158  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:4
2023-03-07 16:59:40.820  INFO 17512 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : end:48,num:5
2023-03-07 16:59:42.988  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:3
2023-03-07 16:59:43.169  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:4

来分析一下结果:

第一:线程都是以task-thread开头的,所以都是线程池的线程

第二:
核心线程42执行任务1,核心线程44执行任务2,两个核心线程都被占用。
任务3和4进入队列,因为队列容量为2,所以队列满了,因为最大线程数为3,于是启动一个新线程48执行任务5。
线程42完成任务1,然后从队列头部将任务3取出执行。
线程44完成任务2,然后从队列头部将任务4取出执行。
线程48完成任务5
线程42完成任务3
线程44完成任务4

执行结果和我们预想的一样,且这个线程池最多可以同时执行5个任务,再多就要触发饱和策略了。

  • 6个并发输出:
2023-03-07 17:16:31.478  INFO 18636 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:1
2023-03-07 17:16:31.683  INFO 18636 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:2
2023-03-07 17:16:32.313  INFO 18636 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : start:48,num:5
Exception in thread "Thread-12" org.springframework.core.task.TaskRejectedException: Executor [java.util.concurrent.ThreadPoolExecutor@68e7e518[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]] did not accept task: org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$743/0x00000008004c3840@29daef23
...
2023-03-07 17:16:34.491  INFO 18636 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:1
2023-03-07 17:16:34.491  INFO 18636 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:3
2023-03-07 17:16:34.689  INFO 18636 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:2
2023-03-07 17:16:34.690  INFO 18636 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:4
2023-03-07 17:16:35.324  INFO 18636 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : end:48,num:5
2023-03-07 17:16:37.503  INFO 18636 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:3
2023-03-07 17:16:37.700  INFO 18636 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:4

从输出结果可以看到,任务6抛了RejectedExecutionException异常。

拒绝策略为DiscardOldestPolicy

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

6个并发输出:

2023-03-07 17:39:59.405  INFO 3344 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:56,num:1
2023-03-07 17:39:59.600  INFO 3344 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:58,num:2
2023-03-07 17:40:00.214  INFO 3344 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : start:62,num:5
2023-03-07 17:40:02.414  INFO 3344 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:56,num:1
2023-03-07 17:40:02.414  INFO 3344 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:56,num:4
2023-03-07 17:40:02.610  INFO 3344 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:58,num:2
2023-03-07 17:40:02.611  INFO 3344 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:58,num:6
2023-03-07 17:40:03.226  INFO 3344 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : end:62,num:5
2023-03-07 17:40:05.421  INFO 3344 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:56,num:4
2023-03-07 17:40:05.616  INFO 3344 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:58,num:6

从上面的输出可以发现,任务3没有被执行。因为任务3最先入队,所以当任务6来的时候饱和按照策略将其删除了。

拒绝策略为CallerRunsPolicy

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

6个并发输出:

2023-03-07 17:40:58.578  INFO 19116 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:53,num:1
2023-03-07 17:40:58.739  INFO 19116 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:55,num:2
2023-03-07 17:40:59.366  INFO 19116 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : start:59,num:5
2023-03-07 17:40:59.580  INFO 19116 --- [      Thread-12] t.s.c.concurrency.ConcurrencyService     : start:60,num:6
2023-03-07 17:41:01.587  INFO 19116 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:53,num:1
2023-03-07 17:41:01.587  INFO 19116 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:53,num:3
2023-03-07 17:41:01.753  INFO 19116 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:55,num:2
2023-03-07 17:41:01.753  INFO 19116 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:55,num:4
2023-03-07 17:41:02.375  INFO 19116 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : end:59,num:5
2023-03-07 17:41:02.587  INFO 19116 --- [      Thread-12] t.s.c.concurrency.ConcurrencyService     : end:60,num:6
2023-03-07 17:41:04.589  INFO 19116 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:53,num:3
2023-03-07 17:41:04.756  INFO 19116 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:55,num:4

从输出可以看到,任务6运行在线程 Thread-12上,这个不是线程池的线程,线程池的线程都是以task-thread开头的。因为线程池的并发是5,所以第6个并发任务按照饱和策略就在调用线程执行了。

拒绝策略为DiscardPolicy

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

6个并发输出:

2023-03-07 16:59:36.944  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:1
2023-03-07 16:59:37.144  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:2
2023-03-07 16:59:37.819  INFO 17512 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : start:48,num:5
2023-03-07 16:59:39.975  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:1
2023-03-07 16:59:39.976  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : start:42,num:3
2023-03-07 16:59:40.158  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:2
2023-03-07 16:59:40.158  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : start:44,num:4
2023-03-07 16:59:40.820  INFO 17512 --- [  task-thread-3] t.s.c.concurrency.ConcurrencyService     : end:48,num:5
2023-03-07 16:59:42.988  INFO 17512 --- [  task-thread-1] t.s.c.concurrency.ConcurrencyService     : end:42,num:3
2023-03-07 16:59:43.169  INFO 17512 --- [  task-thread-2] t.s.c.concurrency.ConcurrencyService     : end:44,num:4

第6个任务被默默的拒绝了,没有被执行。

@Async 使用

我们知道,Spring 使用动态代理来使@Async其作用,所以要求其修饰的方法必须为public级别,且不能在同一个类调用。其修饰的方法返回值必须是void或者Future。所以在必要的时候,我们可以返回CompletableFuture,然后使用其强大的功能完成异步工作。

    @Async
    public CompletableFuture<String> getFirstName() {
        log.info("start get first name");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return CompletableFuture.completedFuture("shusheng");
    }

    @Async
    public CompletableFuture<String> getLastName() {
        log.info("start get last name");
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return CompletableFuture.completedFuture("007");
    }

CompletableFuture 是处理异步编程非常强大的工具,我们应该在合适的时机优先使用。

总结

当前由于框架的广泛使用,程序员并发编程的机会其实没有那么多,但是掌握其知识却是基本功

源码

一如既往,你可以从文章首发找到源码:秒懂SpringBoot之@Async如何自定义线程池

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

秒懂SpringBoot之@Async如何自定义线程池 的相关文章

  • 论 "张筱雨"

    当今网络谁最红 xff1f 当今网络谁最裸 xff1f 当今网络谁征服了大多数男人们的眼球 xff1f 当今网络谁最具创新 xff0c 引领时尚潮流 xff0c 当今网络谁敢和日本AV女优叫板 那么她就是 张筱雨 张筱雨 xff01 xff
  • Linux的多用户登录

    1 Windows只允许在同一时刻只有一个用户登录 xff0c 而Linux允许多用户同时登录 2 在Linux下 xff0c 用户有两种登录模式 一种是 图形界面登录 xff0c 类似于Windows 另一种是 文本登录 xff0c 类似
  • URI encode与URL encode对空格的不同处理

    在各种开放平台 OpenAPI越来越多之时 xff0c 客户端软件或多或少的在通过HTTP HTTPS协议与服务器交互 这其中一个常见的情况就是要求对URL进行encode处理 xff0c 以保证中文 特殊字符的正确传递 不过这个小小的en
  • No plugin found for prefix 'install' in the current project解决方案

    本来以下配置 xff0c 报上面的错 lt mirrors gt lt mirror Specifies a repository mirror site to use instead of a given repository The r
  • android的onNewIntent

    1 onNewIntent Intent intent 是Activity类的方法 它被调用发几种情况如下 lt activity android name 61 34 NewIntentDemo 34 android label 61 3
  • 全网火爆,JMeter性能测试-压力负载测试场景(总结)看这一篇足够了......

    目录 xff1a 导读 前言一 Python编程入门到精通二 接口自动化项目实战三 Web自动化项目实战四 App自动化项目实战五 一线大厂简历六 测试开发DevOps体系七 常用自动化测试工具八 JMeter性能测试九 总结 xff08
  • Linux LVM在线扩容xfs文件系统(创建大于2T的磁盘分区)

    1 使用parted方式格式化磁盘 1 1 查看磁盘情况 root 64 superman fdisk l Disk dev sdd 2199 0 GB 2199023255552 bytes 4294967296 sectors Unit
  • Linux LVM在线扩容ext3文件系统

    1 扩容前信息查看 1 2 查看文件系统情况 root 64 superman df h 文件系统 容量 已用 可用 已用 挂载点 dev mapper VG00 lv root 30G 23G 5 3G 82 dev mapper VG0
  • Linux云计算-04_Linux用户及权限管理

    Linux是一个多用户的操作系统 xff0c 引入用户 xff0c 可以更加方便管理Linux服务器 xff0c 系统默认需要以一个用户的身份登录 xff0c 而且在系统上启动进程也需要以一个用户身份器运行 xff0c 用户可以限制某些进程
  • 01 openEuler操作系统介绍

    文章目录 01 openEuler操作系统介绍1 1 发布件1 2 最小硬件要求1 3 硬件兼容性1 4 关键特性1 4 1 openEuler 22 03 LTS基于 Linux Kernel 5 10 内核构建 在进程调度 内存管理等方
  • 02 openEuler操作系统的安装

    文章目录 02 openEuler操作系统的安装2 1 openEuler操作系统的安装流程2 2 openEuler操作系统的安装详细步骤2 2 1 下载地址2 2 2 创建虚拟机2 2 2 1 方法一 xff1a 典型配置2 2 2 2
  • 06 openEuler XFCE 桌面环境的安装和使用

    06 openEuler XFCE 桌面环境的安装和使用 文章目录 06 openEuler XFCE 桌面环境的安装和使用6 1 XFCE简介6 2 XFCE安装方法6 2 1 更新软件源6 2 2 安装字库6 2 3 安装Xorg6 2
  • 21 openEuler 管理服务-改变运行级别

    文章目录 21 管理服务 改变运行级别21 1 Target和运行级别21 2 查看系统默认启动目标21 3 查看当前系统所有的启动目标21 4 改变默认目标21 5 改变当前目标21 6 切换到救援模式21 7 切换到紧急模式 21 管理
  • 字符串通配(动态规划java)

    1 牛客网题目 xff1a 题目描述 对于字符串A xff0c 其中绝对不含有字符 和 再给定字符串B xff0c 其中可以含有 或 xff0c 字符不能是B的首字符 xff0c 并且任意两个 字符不相邻 exp中的 代表任何一个字符 xf
  • 26 openEuler管理网络-使用ip命令配置网络

    文章目录 26 openEuler管理网络 使用ip命令配置网络26 1 配置IP地址26 1 1 配置静态地址26 1 2 配置多个地址 26 2 配置静态路由 26 openEuler管理网络 使用ip命令配置网络 说明 xff1a 使
  • 31 openEuler使用LVM管理硬盘-管理物理卷

    文章目录 31 openEuler使用LVM管理硬盘 管理物理卷31 1 创建物理卷31 2 查看物理卷31 3 修改物理卷属性31 4 删除物理卷 31 openEuler使用LVM管理硬盘 管理物理卷 31 1 创建物理卷 可在root
  • 41 openEuler搭建FTP服务器-传输文件

    文章目录 41 openEuler搭建FTP服务器 传输文件41 1 概述41 2 连接服务器41 3 下载文件41 4 上传文件41 5 删除文件41 6 断开服务器 41 openEuler搭建FTP服务器 传输文件 41 1 概述 这
  • 45 openEuler搭建Nginx服务器-Nginx概述和安装

    文章目录 45 openEuler搭建Nginx服务器 Nginx概述和安装45 1 概述45 2 安装 45 openEuler搭建Nginx服务器 Nginx概述和安装 45 1 概述 Nginx 是一款轻量级的 Web 服务器 反向代
  • 14 KVM虚拟机配置-配置虚拟设备(其它常用设备)

    文章目录 14 KVM虚拟机配置 配置虚拟设备 xff08 其它常用设备 xff09 14 1 概述14 2 元素介绍14 3 配置示例 14 KVM虚拟机配置 配置虚拟设备 xff08 其它常用设备 xff09 14 1 概述 除存储设备
  • 15 KVM虚拟机配置-体系架构相关配置

    文章目录 15 KVM虚拟机配置 体系架构相关配置15 1 概述15 2 元素介绍15 3 AArch64架构配置示例15 4 x86 64架构配置示例 15 KVM虚拟机配置 体系架构相关配置 15 1 概述 XML中还有一部分体系架构相

随机推荐

  • 16 KVM虚拟机配置-其他常见配置项

    文章目录 16 KVM虚拟机配置 其他常见配置项16 1 概述16 2 元素介绍16 3 配置示例 16 KVM虚拟机配置 其他常见配置项 16 1 概述 除系统资源和虚拟设备外 xff0c XML配置文件还需要配置一些其他元素 xff0c
  • windows查看默认编码类型

    xfeff xfeff 开始 cmd 运行chcp 你会得到一个数 例 xff1a 如936 xff0c 那就是GBK简体中文 ANSI代码页为1252 xff0c 日文代码页为932
  • 三、@PathVariable

    3 1 64 PathVariable 映射 URL 绑定的占位符 带占位符的 URL 是 Spring3 0 新增的功能 xff0c 该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义通过 64 PathVari
  • 走进CSDN

    走进CSDN 关注CSDN不久 xff0c 最近浏览的次数增多 xff0c CSDN的资讯刚开始基本上看的懂的不多 xff0c 专业名词扎堆的论坛 xff0c CSDN的氛围个人感觉挺好的 xff0c 一群俗称 程序员 的人聚集讨论问题 x
  • B端产品经理基本工作流程

    产品岗位必备素质 产品是一个门槛较低的岗位 xff0c 是一个看起来很容易 xff0c 做起来各个地方都是bug的岗位 产品需要更多的是软实力 xff0c 把握产品的方向 xff0c 目标用户是谁 xff0c 场景是什么 xff0c 达到怎
  • Mac实用的远程ssh连接工具( Royal TSX安装及使用)

    Mac实用的远程ssh连接工具 Royal TSX安装及使用 1 下载地址 https www royalapps com ts mac download 2 如何连接远程服务器 2 1 首先下载插件Terminal 2 2 然后创建新的D
  • 尝试VC控制外部程序

    这两天尝试VC控制外部程序呢 xff0c 慢慢完善 在参考了网络学习以后 xff0c 简单做了以下工作 xff1a 期间用了spy 43 43 器件 void CVCControlDlg OnStartreader 启动朗读女 TODO A
  • Windows Sever 2012 R2设置组策略对“不显示最后的登录名”选项已启用

    Windows Sever 2012 R2设置组策略对 不显示最后的登录名 选项已启用 作者 xff1a 我道梦 关注我的CSDN博客 xff0c 更多笔记知识还在更新 xff01 设置组策略启用 不显示最后的登录名 后 xff0c 系统将
  • Ubuntu22.04.1 & WIN11 双系统+双硬盘 grub启动项中无WIN11开机引导

    本机UEFI 43 GPT安装的双系统 xff0c 两块固态硬盘 xff0c 两个系统各自使用自己的硬盘分区 xff0c xff08 选择的全盘安装在新硬盘 xff0c 没有自定义分区 xff0c 所以安装的时候也没有提示与当前window
  • tightvnc,tightvnc软件介绍,详细介绍

    tightvnc一款用于windows操作系统的应用软件 xff0c 是一款远程控制软件 出门在外忘了带档案怎么办 xff1f FTP server 上头忘了开帐号怎么办 xff1f 这些麻烦的问题其实都可以靠 VNC 解决 tightvn
  • OpenCore-EFI-配置模版(持续更新)

    前言 随着OpenCore日臻完善 xff0c 将在以后会更多的用于黑苹果的安装 同时 xff0c 在各位大佬的大力支持与推广 xff0c 各种入门 xff0c 进阶教程的推出 xff0c OpenCore已经从神界降临到人间 逐渐为普通黑
  • OpenCore(OC)引导开机声音与图形界面设置

    关键字 xff1a OC xff1b OpenCore xff1b 引导 xff1b 开机声音 xff1b 图形界面 下面的设置基于OpenCore0 5 8 04 10编译版与1 22 0 0版OpenCore Configurator
  • The BMJ研究:现有的新冠病毒诊断AI模型,几乎毫无用处

    图片出处 xff1a unsplash 本文作者 xff1a 朱演瑞 新型冠状病毒对全球健康造成了严重的威胁 xff0c 为了减轻医疗保健系统的负担 xff0c 也给患者提供最佳的护理 xff0c 高效的诊断和疾病预后信息问题亟待解决 理论
  • 06-Docker-Centos 7.2 (Vmware最小化安装)之一篇搞定hyperledger/fabric的e2e_cli测试运行所遇到的ERROR总结

    bug产生原因分析如下 xff1a 1 系统过于单纯或复杂 xff08 即最小化安装与全部安装以及自行安装了很多软件 xff09 xff0c 很多命令和工具无法使用和执行或冲突 2 自己操作失误 xff0c 敲错代码 xff08 关键词和语
  • 秒懂Java之方法引用(method reference)详解

    版权申明 非商业目的注明出处可自由转载 出自 xff1a shusheng007 相关文章 xff1a 秒懂Java之深入理解Lambda表达式 文章目录 概述使用条件使用场景如何使用方法引用的类型调用类的静态方法调用传入的实例参数的方法调
  • 产品设计中关于思考力那些事

    这周的面试 xff0c 对我自己来说 xff0c 更像是一种迭代反思 从做什么怎么做 xff0c 到为什么做 xff0c 的一种强制思考 一方面是入行时间短 xff0c 另一方面是公司产品业务主导 xff0c 相对不需要产品去思考 xff0
  • 永不磨灭的设计模式(有这一篇真够了,拒绝标题党)

    版权申明 非商业目的注明出处可自由转载 出自 xff1a shusheng007 文章目录 概述定义分类创建型 xff08 creational xff09 结构型 xff08 structural xff09 行为型 xff08 beha
  • shusheng007编程手记

    版权申明 非商业目的注明出处可自由转载 出自 xff1a shusheng007 文章目录 概述工具篇IntelliJ IDEA在Idea中下载源码时 xff0c 报无法下载源码 Postman使用Postman发送Post请求服务端报得到
  • SpringBoot如何整合RabbitMQ

    版权申明 非商业目的注明出处可自由转载 出自 xff1a shusheng007 文章目录 概述rabbitmq简介SpringBoot整合安装rabbitmq初级用法高级用法配置交换器与队列发送消息消费消息测试 总结 概述 好久没有写博客
  • 秒懂SpringBoot之@Async如何自定义线程池

    版权申明 非商业目的注明出处可自由转载 出自 xff1a shusheng007 文章目录 概述异步初探线程池ThreadPoolExecutorThreadPoolTaskExecutor 验证线程池配置拒绝策略为AbortPolicy拒