线程池对于Java程序开发已经不模式,用得也比较广泛,下面讲解多线程以及线程池在SpringBoot项目中的使用。
当我们开启一个SpringBoot的项目,我们并未设置任何和线程相关的操作,但是我们的程序还是可以执行多个请求。甚至说绝大多数的项目,我们不需要对线程这块做任何操作。
但是如果是单线程的话,它显然满足不了我们系统的需求,所有我们有必要了解一下,它默认的线程情况。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public String fun(){
System.out.println(Thread.currentThread().getName());
return "success";
}
}
打印结果如下:我请求了14次。
http-nio-8888-exec-1
http-nio-8888-exec-2
http-nio-8888-exec-3
http-nio-8888-exec-4
http-nio-8888-exec-5
http-nio-8888-exec-8
http-nio-8888-exec-7
http-nio-8888-exec-6
http-nio-8888-exec-9
http-nio-8888-exec-10
http-nio-8888-exec-1
http-nio-8888-exec-2
http-nio-8888-exec-3
http-nio-8888-exec-4
可以看到它默认是有10个线程去执行我们的任务的。这个其实就是tomcat的默认线程我们可以在yml/properties里面进行配置。
server:
port: 8888
tomcat:
min-spare-threads: 10
max-threads: 200
我们可以全局搜索一下这个min-spare-threads,这个json就是Sping的一些默认配置。可以看到里面配置了tomcat默认的线程数是10,最大线程数是200,而对于一般项目来说,这两个数字都已经够用了。
1-2、定时任务默认线程
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling
public class XdxOne {
@Scheduled(cron = "*/1 * * * * ?")
public void testOne() throws Exception {
System.out.println("one " + " "+ Thread.currentThread().getName());
}
}
打印结果如下
one scheduling-1
one scheduling-1
one scheduling-1
one scheduling-1
one scheduling-1
one scheduling-1
one scheduling-1
one scheduling-1
我们也可以在上面那个json里面找到这样的一个配置默认的定时任务是一个线程去执行的
{
"name": "spring.task.scheduling.pool.size",
"type": "java.lang.Integer",
"description": "Maximum allowed number of threads.",
"sourceType": "org.springframework.boot.autoconfigure.task.TaskSchedulingProperties$Pool",
"defaultValue": 1
}
corePoolSize:核心线程数;maximunPoolSize:最大线程数
每当有新的任务到线程池时
第一步:先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步;
第二步:判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;
第三步:判断线程池中的线程数量是否达到了maxumunPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。注意:在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
由第三步可知,在一般情况下,Java线程池中会长期保持corePoolSize个线程。
当工作队列满且线程个数达到maximunPoolSize后所采取的策略
AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
DiscardPolicy:丢弃新的任务,且不抛出异常。
DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
自定义策略:根据用户需要定制。
三、线程异步执行
在SpringBoot里面异步执行的很简单,只需要加上一个注解就行了 @Async需要先加上开启异步的注解,在任何地方加上都可以。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling
@Async
public class XdxOne {
@Scheduled(cron = "*/1 * * * * ?")
public void testOne() throws Exception {
System.out.println("one " + " "+ Thread.currentThread().getName());
}
}
异步执行结果
one task-1
one task-2
one task-3
one task-4
one task-5
one task-6
one task-7
one task-8
one task-1
one task-2
one task-3
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Async
public class TestController {
@GetMapping("/test")
public String fun(){
System.out.println(Thread.currentThread().getName());
return "success";
}
}
执行结果
task-1
task-2
task-3
task-4
task-5
task-6
task-7
task-8
task-1
{
"name": "spring.task.execution.pool.core-size",
"type": "java.lang.Integer",
"description": "Core number of threads.",
"sourceType": "org.springframework.boot.autoconfigure.task.TaskExecutionProperties$Pool",
"defaultValue": 8
},
{
"name": "spring.task.execution.thread-name-prefix",
"type": "java.lang.String",
"description": "Prefix to use for the names of newly created threads.",
"sourceType": "org.springframework.boot.autoconfigure.task.TaskExecutionProperties",
"defaultValue": "task-"
},
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
// 最大线程数
executor.setMaxPoolSize(50);
// 队列最大长度
executor.setQueueCapacity(1000);
// 线程池维护线程所允许的空闲时间
executor.setKeepAliveSeconds(100);
// 线程前缀
executor.setThreadNamePrefix("AsyncExecutorThread-");
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new CallerRunsPolicy());
executor.initialize();
return executor;
}
}
我们创建了上面的线程池后,所有的任务都将使用这个线程池里面的线程(可以通过输出线程名查看)
但是如果我们有多个线程池的时候,这个时候我们可以使用@Async(“name”)name就是线程池的名字,没有写name的时候默认使用bean为taskExecutor的线程池。
因为上面我们bean的名字就是taskExecutor,所以我们的其它操作都不用改变,只需要在代码里面加上上面这个文件即可,再次运行之前的代码,我们发现打印的线程就已经是我们自定义的线程池了。
默认的任务是使用tomcat默认的线程池去执行,可以在yml/properties里面进行配置。(默认核心线程数:10,最大线程数:200)
默认的定时任务是使用一个单线程去执行。
我们只需要使用@Async和@EnableAsync就可以开启异步执行。
如果没有自定义线程池,将会使用默认的线程池。(SpringBoot的版本不同默认的线程池也不同)
如果自定义了线程池,将会使用我们自定义的线程池。默认使用bean为taskExecuto的线程池。
React开发需要基于NodeJS的环境,建议尽量选择高一点的版本,不然建项目时会提示版本过低。安装完成后,默认会自动配置环境变量,使用 node -v 命令即可查看 NodeJS 版本信息,Windows 和 MacOS上命令一致; 环境安装完成,接下来通过脚手架的方式生成 React 项目,由于国内使用 nmp 方式速度太慢,建议使用淘宝镜像的 cnpm 形式(如果有特殊渠道,可以忽略)。接下
安全绝不收集任何用户、服务器信息,是一个独立安全的服务 Gokins 官网 地址 :http://gokins.Step 2: 启动服务 Step 3: 查看服务 访问http://localhost:8030 使用gokins 下载运行 github地址 :https://github.com/mgr9525/gokins 可在对应平台需找发行版 或者直接在服务器上执行以下命令 运行成功后 访问
针对 naturalOrder() ,API 的介绍是这么写的:“Returns a comparator that compares {@link Comparable} objects in natural”。说白了就是将之前提倡的 compare 比较表达式给替换了,改由 Comparator.源码参考: 在编码中,如果我们使用了 compare 比较表达式,即“(e1, e2) -> e1
很多项目也使用了 mybatis 持久层,默认的配置下,是不会打印 SQL 日志的,对于开发环境不太友好。如果想要在控制台打印出 SQL 日志,在配置文件中加入一行代码即可: 很多教程可能会这么写: 包名 "com.test" 我们为启动类所在的根目录,两种配置方式均可以打印出 SQL 日志,推荐第一种就行了。
Obatis开源框架数据库编程基于子项目obatis-core进行实现,数据库编程代理类为: QueryProvider类提供了丰富的数据库操作API,尽可能满足项目开发中的需要,数据库编程对于表达式方面的操作,考虑到编程规范及代码规范的问题,表达式操作的封装独立于类: 主要提供的API有: 主要提供将表达式结果为null时,转化为0返回,nullToZero()主要用于表达式结构,例如 
如今的Java项目开发,Springboot/Springcloud、Swagger已经成为了标配,下面我们来说说如何在项目中引入Swagger…… 引入Swagger只需要以下3步,即可使用Swagger提供的功能,并不复杂。2、创建SwaggerConfig文件,并且实现 createRestApi() 方法 3、在 Controller 接口文件中,注解 Swagger 信息 通过上面3个步
如果我们有需求需要将Java类的属性及值生成URL格式数据并且按照ASCII码排序,这种需求在项目中特别是和第三方系统进行对接的时候有使用场景。
提供加密,解密,生成密钥对等方法。txt"; /** * * 生成密钥对 * * * @return KeyPair * * @throws EncryptException */ public static KeyPair generateKeyPair() throws Exception { try { KeyPairGenerator keyPairGen = KeyPairGenerat
这里感觉有点坑,如果启动类不加@EnableScheduling 注解,项目中也可以正常配置定时任务时间表达式,但不会执行,大意的开发者可能需要花费更多的调试时间和查询资料。@Scheduled 注解为时间表达式属性配置,告诉Quantz时间调度,我需要在什么时间定时执行这个方法,用白话说就是这么简单。执行结果如下: 下面内容为开发者注意的点,需理解 细心的开发者应该会发现,设置的定时任务是每2分