什么是线程
线程是操作系统中最小的单位。
详解: 进程大家应该略有耳闻吧,进程由n个线程组成,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
什么是线程池
线程池是一种线程使用的模式,线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
详解:线程池相当于一个租车公司,租车公司有固定的车辆(假设30辆),每辆车相当于一个线程,被租出去则是运行中,空着这是闲置中。
线程池常见类别
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduleThreadPool
- newSingleThreadScheduledExecutor
不同的线程池有对应的区别,想详细了解的cxy可以正对性的去学习,本文讲以上解线程池内部都会实现的ThreadPoolExecutor方法,已ThreadPoolTaskExecutor为例。
代码
pom.xml
<!--配置文件加载-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--这边里只给出特别需要的dependency,启动类的基本dependency 就不展示了-->
application.yml
async:
corePoolSize: 3
maxPoolSize: 4
queueCapacity: 1
namePrefix: async-newThreadName-
server:
port: 8088
配置类(Config)
package com.example.qrcode.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
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.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author lyj
* @version 1.0
* @date 2021/1/26 14:19
*/
@Configuration
@EnableAsync
@ConfigurationProperties(prefix = "async")
@Data
public class ExecutorConfig {
private int corePoolSize; //线程池的基本数量
private int maxPoolSize; //线程池维护线程的最大数量(最大数量>=基本数量)
private int queueCapacity; //线程池所使用的缓冲队列(顾名思义等待列队,当全部线程处于运行中时,还有新任务就进列队排队)
private String namePrefix; //线程名字前缀(只是线程名字)
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
System.err.println("初始化线程池-----");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
实现类(Controller)
package com.example.qrcode.utile;
import com.example.qrcode.config.ExecutorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executor;
/**
* @author lyj
* @version 1.0
* @date 2021/1/26 14:06
*/
@RestController
public class AsyncTask {
@Autowired
private Executor asyncServiceExecutor;
private final Integer totalRunNum=20; //运行次数
@RequestMapping(value = "index")
public String index(){
asyncServiceExecutor.execute(
//一个线程的开始
asyncServiceExecutor.execute(
new TimerTask() {
@Override
public void run() {
String name=Thread.currentThread().getName();
for (int i = 1; i <= totalRunNum; i++) {
System.out.println(name+":第"+i+"次测试");
if (i == totalRunNum){
System.out.println(name+"结束。");
}
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
);
//一个线程的结束
asyncServiceExecutor.execute(
new TimerTask() {
@Override
public void run() {
String name=Thread.currentThread().getName();
for (int i = 1; i <= totalRunNum; i++) {
System.out.println(name+":第"+i+"次测试");
if (i == totalRunNum){
System.out.println(name+"结束。");
}
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
);
//...这里我只copy俩个线程根据自己测试需求来 copy
return "线程运行";
}
/**
* @Description: 创建线程示例方法
* @Date: 2021/1/26 19:28
*/
private void asyncRun(){
asyncServiceExecutor.execute(
new TimerTask() {
@Override
public void run() {
String name=Thread.currentThread().getName();
for (int i = 1; i <= totalRunNum; i++) {
System.out.println(name+":第"+i+"次测试");
if (i==totalRunNum){
System.out.println(name+"结束。");
}
}
}
}
);
}
}
execute(Runable)方法执行过程
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maxPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maxPoolSize,那么通过handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
如果没看懂的话看看下面这个(个人理解版):
假设 一个任务需求需要一个线程实现。
corePoolSize 基本线程数量
workQueue 缓冲队列
- 任务需求数量<corePoolSize,需求会被线程相应处理,且会有线程闲置。
- 任务需求数量=corePoolSize,需求会被线程相应处理,无线程闲置。
- 任务需求数量>corePoolSize, 如workQueue未满且能容下剩余需求量,则部分需求会被线程相应处理,剩余任务需求进入列队排队,等待闲置的线程。
- 任务需求数量>corePoolSize,如workQueue已满活不能容下剩余需求量,则部分需求会被线程相应处理,创建临时线程处理(此时总线程数量不会大于最大线程数量)。
显示结果
示例情况1
corePoolSize: 3 基本线程
maxPoolSize: 4 最大线程
queueCapacity: 1 列队
totalRunNum=20; //运行次数
totalNum=3; //实际运行线程数量(任务需求量)
这里只截取了后部分的显示情况,由于测试运行次数少,并发不是很明显,在线程3结束前线程1/2都有运行,基本情况3条线程并发运行!
示例情况2
corePoolSize: 3 基本线程
maxPoolSize: 3 最大线程
queueCapacity: 1 列队
totalRunNum=20; //运行次数
totalNum=4; //实际运行线程数量(任务需求量)
可以明显的看到前面三条线基本线程处理三个任务需求,第四个任务需求进入列队排队,等待线程1结束后,再重新调用线程1来执行第4个需求,符合上述的执行过程。
示例情况3
corePoolSize: 3 基本线程
maxPoolSize: 4 最大线程
queueCapacity: 0 列队
totalRunNum=20; //运行次数
totalNum=4; //实际运行线程数量(任务需求量)
示例3,最大线程是4条,基本是3条,列队不能排队,任务需求是4条,线程池在基本线程的基础上再创建了一条临时线程4,来处理。
示例情况4
corePoolSize: 3 基本线程
maxPoolSize: 3 最大线程
queueCapacity: 0 列队
totalRunNum=20; //运行次数
totalNum=4; //实际运行线程数量(任务需求量)
现在是最大三个线程,且列队不能排队,4个任务需求,线程池最大只能创建3个,所以出现红色框框线程池外的新线程处理的(并不是线程池里创建的)。
制作不易点个赞支持一下,谢谢各位CXY!