关于Spring3.2.4.RELEASE中,使用线程池以及动态监控其运行
线程池简介
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。
一个线程池包括以下四个基本组成部分:
- 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
- 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
- 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
- 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
项目引入线程池
可以自己写线程池,如我自己的线程池https://blog.csdn.net/ycde2009/article/details/84538166,当然也可以使用spring提供的线程池,既然已经有轮子,那就不再重复造轮子了,直接引用spring提供的就可以了。
- applicationContext.xml中配置:
<util:properties id="configProps" location="classpath:properties.properties" />
<!-- 线程池执行Excel任务的自定义配置属性类,从properties中读取 -->
<bean id="excelEduceProperties" name="excelEduceProperties" class="com.daidiacloud.shundai.config.ExcelEduceProperties" scope="prototype" autowire="default">
<property name="maxFileRow" value="#{configProps['excel.educe.maxFileRow']}" />
<property name="cutFileRow" value="#{configProps['excel.educe.cutFileRow']}" />
<property name="multiThreading" value="#{configProps['excel.educe.multiThreading']}" />
</bean>
<!-- 线程池配置 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数,默认为1 -->
<property name="corePoolSize" value="#{configProps['task.corePoolSize']}"></property>
<!-- 最大线程数,默认为Integer.MAX_VALUE -->
<property name="maxPoolSize" value="#{configProps['task.maxPoolSize']}"></property>
<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
<property name="queueCapacity" value="#{configProps['task.queueCapacity']}"></property>
<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="#{configProps['task.keepAliveSeconds']}"></property>
<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
<!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常
CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度
DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行
DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"></bean>
</property>
</bean>
<!-- 监控服务,自定义监控类 -->
<bean id="threadPoolMonitorService" name="threadPoolMonitorService" class="com.daidiacloud.shundai.thread.service.impl.ThreadPoolMonitorService">
<property name="executor" ref="taskExecutor" />
<property name="monitoringPeriod" value="#{configProps['task.monitoringPeriod']}" />
</bean>
- properties.properties:
# 线程池参数
task.corePoolSize=8
task.maxPoolSize=129
task.queueCapacity=72
task.keepAliveSeconds=10
task.monitoringPeriod = 5
# 多线程执行Excel任务的配置
excel.educe.multiThreading = true
excel.educe.cutFileRow = 300
excel.educe.maxFileRow = 60000
- 自定义ExcelEduceProperties.java:
public class ExcelEduceProperties {
/**
* 多线程处理某个文件最大行数
*/
private Integer maxFileRow;
/**
* 多线程处理某个文件时,分割文件的行数
*/
private Integer cutFileRow;
/**
* 是否启用多线程
*/
private Boolean multiThreading;
public ExcelEduceProperties() {
super();
}
public ExcelEduceProperties(Integer maxFileRow, Integer cutFileRow, Boolean multiThreading) {
super();
this.maxFileRow = maxFileRow;
this.multiThreading = multiThreading;
}
public Integer getMaxFileRow() {
if(maxFileRow == null) {
maxFileRow = new Integer(100000);
}
return maxFileRow;
}
public void setMaxFileRow(Integer maxFileRow) {
this.maxFileRow = maxFileRow;
}
public Boolean getMultiThreading() {
if(multiThreading == null) {
multiThreading = false;
}
return multiThreading;
}
public void setMultiThreading(Boolean multiThreading) {
this.multiThreading = multiThreading;
}
public Integer getCutFileRow() {
if(cutFileRow == null) {
cutFileRow = new Integer(100);
}
return cutFileRow;
}
public void setCutFileRow(Integer cutFileRow) {
this.cutFileRow = cutFileRow;
}
}
- 自定义监控接口IThreadPoolMonitorService.java:
public interface IThreadPoolMonitorService extends Runnable {
void monitorThreadPool();
ThreadPoolTaskExecutor getExecutor();
void setExecutor(ThreadPoolTaskExecutor executor);
void init();
void destroy();
DataResponse show();
Boolean getDestroyed();
}
- 自定义监控接口实现类ThreadPoolMonitorService.java:
@Service
public class ThreadPoolMonitorService implements IThreadPoolMonitorService {
private static Logger log = LoggerFactory.getLogger(ThreadPoolMonitorService.class);
private ThreadPoolTaskExecutor executor;
private long monitoringPeriod;
private Boolean destroyed = false;
public void run() {
// 不启用后台监控
/*try {
while (!destroyed){
monitorThreadPool();
Thread.sleep(monitoringPeriod*1000);
}
} catch (Exception e) {
if(log.isErrorEnabled()){
log.error(e.getMessage());
}
}*/
}
public void init() {
executor.initialize();
destroyed = false;
// 不启用后台监控
// new Thread(this).start();
if(log.isInfoEnabled()){
log.info("==> 线程池开启 ============================");
}
}
public void destroy() {
executor.destroy();
this.destroyed = true;
if(log.isInfoEnabled()){
log.info("==> 线程池销毁 ============================");
}
}
@Override
public DataResponse show() {
DataResponse dataResponse = new DataResponse();
ThreadPoolMonitorVo vo = new ThreadPoolMonitorVo(executor.getThreadPoolExecutor().getPoolSize(),
executor.getThreadPoolExecutor().getCorePoolSize(),
executor.getThreadPoolExecutor().getMaximumPoolSize(),
executor.getThreadPoolExecutor().getLargestPoolSize(),
executor.getThreadPoolExecutor().getActiveCount(),
executor.getThreadPoolExecutor().getQueue().size(),
executor.getThreadPoolExecutor().getCompletedTaskCount(),
executor.getThreadPoolExecutor().getTaskCount(),
this.destroyed);
dataResponse.setResponse(vo);
return dataResponse;
}
public void monitorThreadPool() {
StringBuffer strBuff = new StringBuffer()
.append("\n - ThreadPoolExecutor : ")
.append("\n | - 关闭状态 - Destroyed : ").append(this.destroyed)
.append("\n | - 当前线程数 - PoolSize : ").append(executor.getThreadPoolExecutor().getPoolSize())
.append("\n | - 核心线程数 - CorePoolSize : ").append(executor.getThreadPoolExecutor().getCorePoolSize())
.append("\n | - 线程池最大线程数 - MaximumPoolSize : ").append(executor.getThreadPoolExecutor().getMaximumPoolSize())
.append("\n | - 历史线程数最大峰值 - LargestPoolSize : ").append(executor.getThreadPoolExecutor().getLargestPoolSize())
.append("\n | - 正在执行任务数 - ActiveCount : ").append(executor.getThreadPoolExecutor().getActiveCount())
.append("\n | - 队列缓存任务数 - QueueSize : ").append(executor.getThreadPoolExecutor().getQueue().size())
.append("\n | - 累计完成任务数 - CompletedTaskCount : ").append(executor.getThreadPoolExecutor().getCompletedTaskCount())
.append("\n | - 累计任务总数 - TaskCount : ").append(executor.getThreadPoolExecutor().getTaskCount());
if(log.isDebugEnabled()){
log.debug(strBuff.toString());
}
}
public ThreadPoolTaskExecutor getExecutor() {
return executor;
}
public void setExecutor(ThreadPoolTaskExecutor executor) {
this.executor = executor;
}
public long getMonitoringPeriod() {
return monitoringPeriod;
}
public void setMonitoringPeriod(long monitoringPeriod) {
this.monitoringPeriod = monitoringPeriod;
}
public Boolean getDestroyed() {
return destroyed;
}
public void setDestroyed(Boolean destroyed) {
this.destroyed = destroyed;
}
}
线程池参数设置
- 高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换;
- 并发不高、任务执行时间长的业务,需要看情况;
a. 假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务;
b.假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换。 - 并发高、业务执行时间长,这得从业务流程、架构方面来优化了:
a. 分析这些业务流程里面,某些数据是否能做缓存,引入redis等第三方的缓存中间件;
b. 堆服务器,增加整体吞吐性能;
c. 除了a、b之外,执行时间长的业务,从其他角度来优化,如集合的选用,字符串处理,业务拆分、解耦等。
编写监听器,随容器启动、停止线程池
- 编写接听器ThreadPoolListener.java,实现javax.servlet.ServletContextListener接口:
public class ThreadPoolListener implements ServletContextListener {
private static Logger LOG = LoggerFactory.getLogger(ThreadPoolListener.class);
@Override
public void contextDestroyed(ServletContextEvent arg0) {
WebApplicationContext appctx = WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext());
IThreadPoolMonitorService threadPoolMonitorService = (IThreadPoolMonitorService) appctx
.getBean("threadPoolMonitorService");
threadPoolMonitorService.destroy();
if(LOG.isInfoEnabled()){
LOG.info("==> 线程池监听器结束 ============================");
}
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
if(LOG.isInfoEnabled()){
LOG.info("==> 线程池监听器启动 ============================");
}
WebApplicationContext appctx = WebApplicationContextUtils.getWebApplicationContext(arg0.getServletContext());
IThreadPoolMonitorService threadPoolMonitorService = (IThreadPoolMonitorService) appctx
.getBean("threadPoolMonitorService");
threadPoolMonitorService.init();
}
- web.xml中配置监听器,加入ThreadPoolListener.java
<listener>
<listener-class>
com.daidiacloud.shundai.web.listener.ThreadPoolListener
</listener-class>
</listener>
编写多线程任务接口的实现类
- 通过使用CountDownLatch对象,达到获取线程池处理任务后的所有结果。
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。CountDownLatch能够使一个线程在等待另外一个或多个线程完成各自工作之后,再继续执行主线程余下的代码。使用一个计数器进行实现,计数器初始值为线程的数量,当每一个线程完成自己任务后,计数器的值就会减1。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。 - 编写自定义任务类ExcelEduceThreadTask.java,实现java.util.concurrent.Callable接口
public class ExcelEduceThreadTask implements Callable<List<String>> {
private static Logger logger = Logger.getLogger(ExcelEduceThreadTask.class);
private List<String> strList;
private CountDownLatch endSigle;
public ExcelEduceThreadTask(List<String> strList, CountDownLatch endSigle) {
this.strList = strList;
this.endSigle = endSigle;
}
/**
* 执行具体任务
*/
@Override
public List<String> call() throws Exception {
if (logger.isDebugEnabled()) {
logger.debug(" ---- ---- ---- strat Thread name : " + Thread.currentThread().getName()
+ " ---- ---- ---- ID :" + Thread.currentThread().getId());
}
// 返回的执行结果
List<String> result = new ArrayList<String>();
// 执行具体逻辑
result.add("success");
// 执行结束后
endSigle.countDown();
if (logger.isDebugEnabled()) {
logger.debug(" ---- ---- ---- end Thread name : " + Thread.currentThread().getName()
+ " ---- ---- ---- ID :" + Thread.currentThread().getId());
}
return result;
}
}
调用线程池处理任务
一般是在自定义业务实现类中,调用多线程处理具体任务。
- 自定义业务实现类DemoServiceImpl.java中定义成员变量:
@Autowired
private ExcelEduceProperties excelEduceProperties;
@Autowired
private IThreadPoolMonitorService threadPoolMonitorService;
- 自定义业务实现类DemoServiceImpl.java中使用线程池,这里处理excel集合的数据:
public List<String> exeBatch(){
// 待处理的数据,一搬从前台传来的Excel获取,这里只做定义
List<String> resourceList = new ArrayList<>();
// 按设定的数据行数,来分隔excel集合,如CutFileRow=100,就按照100行作为一个子任务的数据
int eCount = excelEduceProperties.getCutFileRow().intValue();
/* 按照CutFileRow条的数量,来切割去重后的JavaBean集合。
相当于每CutFileRow条作为一个集合放到,以元素的身份放到另一个List集合中 */
List<List<String>> excelBatchList = new ArrayList<List<String>>();
for(int i = 0; i < resourceList.length; i++){
String excelStringTemp = resourceList.get(i);
// 取模为0,新加入一个集合
if ((i % eCount) == 0) {
excelBatchList.add(new ArrayList<String>());
}
excelBatchList.get((i / eCount)).add(excelStringTemp);
}
// 每个线程返回的结果,添加进resultFutureSync
List<Future<List<String>>> resultFutureSync =
Collections.synchronizedList(new ArrayList<Future<List<String>>>());
// 所有线程执行结束后,我们把每个线程返回的resultFutureSync结果,遍历后存入resultList
List<String> resultList =
new ArrayList<String>();
// 需要开启的线程数
int threadCount = excelBatchList.size();
// 线程池,每个线程执行完毕的计数器
CountDownLatch latch = new CountDownLatch(threadCount);
try {
// 遍历出大集合excelBatchList,每一个下标,对应开启一个线程。
for (List<String> threadList : excelBatchList) {
ExcelEduceThreadTask task = new ExcelEduceThreadTask(
threadList,
latch
);
if (threadPoolMonitorService.getDestroyed()) {
throw new ServiceException("执行失败,线程池已关闭!");
} else {
Future<List<String>> future = threadPoolMonitorService.getExecutor().submit(task);
resultFutureSync.add(future);
}
}
latch.await();
for (Future<List<String>> temp : resultFutureSync) {
resultList.addAll(temp.get());
}
} catch (SalvaExceptionBase e) {
// TODO Auto-generated catch block
logger.info(e);
throw e;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
logger.info(e);
throw new ServiceException("写入失败!");
}
// 返回线程池处理结果
return resultList;
}
配置监控页面
前端主要使用bootstarp、jquery、echarts来做,开关样式是titatoggle,通过Ajax轮询请求线程池状态,并提供启动和销毁线程池的接口。
- index.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta charset="utf-8"/>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<title>线程池管理</title>
</head>
<jsp:include page="../../common/common.jsp"/>
<link rel="stylesheet"
href="${sessionScope.portalContextPath}/content/plupload-2.1.2/js/jquery.plupload.queue/css/jquery.plupload.queue.css"
type="text/css" media="screen"/>
<link rel="stylesheet"
href="${sessionScope.portalContextPath}/content/assets/css/bootstrap.min.css"
type="text/css"/>
<link rel="stylesheet"
href="${sessionScope.portalContextPath}/content/css/titatoggle-dist-min.css"
type="text/css"/>
<style>
.switch-text {
font-size: 18px;
text-align: center;
margin-top: 10px;
}
.switch-text-left {
}
.my-echarts {
margin-left: 5%;
width: 90%;
height: 380px;
}
</style>
<body>
<div class="container-fluid">
<div class="row" style="margin-top: 30px">
<div class="col-md-3">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">操作面板</h3>
</div>
<div class="panel-body">
<div class="col-md-5 col-md-offset-1 ">
<div class="switch-text switch-text-left">
线程池开关
<div id = "serviceError" style="display: none" class="alert alert-danger" role="alert">服务器异常,请刷新页面。</div>
</div>
</div>
<div class="col-md-3">
<div class="checkbox checkbox-slider--b-flat checkbox-slider-md">
<label>
<input id="threadPoolSwitch" type="checkbox"><span></span>
</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div id="myPanelEcharts" class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">线程状态</h3>
</div>
<div class="panel-body">
<div id="main" class="my-echarts"></div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-3">
当前线程数 :
</div>
<div class="col-md-2">
<span id="poolSize" class="badge">0</span>
</div>
<div class="col-md-4 col-md-offset-1">
核心线程数 :
</div>
<div class="col-md-2">
<span id="corePoolSize" class="badge">0</span>
</div>
</div>
<div class="row">
<div class="col-md-3">
最大线程数 :
</div>
<div class="col-md-2">
<span id="maximumPoolSize" class="badge">0</span>
</div>
<div class="col-md-4 col-md-offset-1">
历史最大峰值 :
</div>
<div class="col-md-2">
<span id="largestPoolSize" class="badge">0</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div id="myPanelEchartsTask" class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">任务状态</h3>
</div>
<div class="panel-body">
<div id="mainTask" class="my-echarts"></div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-4">
正在执行任务数 :
</div>
<div class="col-md-2">
<span id="activeCount" class="badge">0</span>
</div>
<div class="col-md-4">
队列缓存任务数 :
</div>
<div class="col-md-2">
<span id="queueSize" class="badge">0</span>
</div>
</div>
<div class="row">
<div class="col-md-4">
累计完成任务数 :
</div>
<div class="col-md-2">
<span id="completedTaskCount" class="badge">0</span>
</div>
<div class="col-md-4">
累计任务总数 :
</div>
<div class="col-md-2">
<span id="taskCount" class="badge">0</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<jsp:include page="../../common/footer-1.jsp"/>
<script type="text/javascript"
src="${sessionScope.portalContextPath}/content/plupload-2.1.2/js/plupload.full.min.js"></script>
<script type="text/javascript"
src="${sessionScope.portalContextPath}/content/plupload-2.1.2/js/i18n/zh_CN.js"></script>
<script type="text/javascript"
src="${sessionScope.portalContextPath}/content/plupload-2.1.2/js/jquery.plupload.queue/jquery.plupload.queue.js"></script>
<script type="text/javascript"
src="${sessionScope.portalContextPath}/content/assets/js/bootstrap.min.js"></script>
<script type="text/javascript"
src="${sessionScope.portalContextPath}/content/echarts/echarts.min.js"></script>
<jsp:include page="../../common/footer-2.jsp"/>
<script type="text/javascript">
$(function () {
show();
$("#threadPoolSwitch").change(function () {
if ($("#threadPoolSwitch").is(':checked')) {
$.ajax({
url: '${pageContext.request.contextPath}/threadPoolMonitor/init.do',
type: 'get',
success: function (data) {}
});
}else{
$.ajax({
url: '${pageContext.request.contextPath}/threadPoolMonitor/destroy.do',
type: 'get',
success: function (data) {}
});
}
});
});
// 基于准备好的dom,初始化echarts实例
const myChart = echarts.init(document.getElementById('main'));
const myChartTask = echarts.init(document.getElementById('mainTask'));
option = {
tooltip: {
formatter: '{a} <br/>激活线程/最大线程 : {c}%'
},
toolbox: {
feature: {
restore: {},
saveAsImage: {}
}
},
series: [
{
name: '线程指标',
type: 'gauge',
detail: {formatter: '{value}%'},
data: [{value: 0, name: '最大线程比'}]
}
]
};
optionTask = {
tooltip: {
formatter: '{a} <br/>激活任务/未完成任务 : {c}%'
},
toolbox: {
feature: {
restore: {},
saveAsImage: {}
}
},
series: [
{
name: '任务指标',
type: 'gauge',
detail: {formatter: '{value}%'},
data: [{value: 0, name: '激活待处理比率'}]
}
]
};
myChart.setOption(option, true);
myChartTask.setOption(optionTask, true);
var showInterval = setInterval(function () {
show();
}, 10000);
function show() {
$.ajax({
url: '${pageContext.request.contextPath}/threadPoolMonitor/show.do',
type: 'get',
async: false,
success: function (data) {
if(!data || !data.response){
clearInterval(showInterval);
$("#serviceError").show();
return;
}
if(!data.response.destroyed){
$("#threadPoolSwitch").attr("checked","checked");
}else{
$("#threadPoolSwitch").removeAttr("checked");
}
var poolSize = data.response.poolSize;
var corePoolSize = data.response.corePoolSize;
var maximumPoolSize = data.response.maximumPoolSize;
var largestPoolSize = data.response.largestPoolSize;
var activeCount = data.response.activeCount;
var queueSize = data.response.queueSize;
var completedTaskCount = data.response.completedTaskCount;
var taskCount = data.response.taskCount;
$("#poolSize").html(poolSize);
$("#corePoolSize").html(corePoolSize);
$("#maximumPoolSize").html(maximumPoolSize);
$("#largestPoolSize").html(largestPoolSize);
$("#activeCount").html(activeCount);
$("#queueSize").html(queueSize);
$("#completedTaskCount").html(completedTaskCount);
$("#taskCount").html(taskCount);
var optionValue = 0;
if (maximumPoolSize > 0) {
optionValue = ((poolSize / maximumPoolSize) * 100).toFixed(2) - 0;
}
option.series[0].data[0].value = optionValue;
myChart.setOption(option, true);
var optionTaskValue = 0;
if ((activeCount + queueSize) > 0) {
optionTaskValue = ((activeCount / (activeCount + queueSize)) * 100).toFixed(2) - 0;
}
optionTask.series[0].data[0].value = optionTaskValue;
myChartTask.setOption(optionTask, true);
if (optionValue < 20) {
$("#myPanelEcharts").attr("class", "panel panel-success");
} else if (optionValue < 80) {
$("#myPanelEcharts").attr("class", "panel panel-info");
} else {
$("#myPanelEcharts").attr("class", "panel panel-danger");
}
if (optionTaskValue < 20) {
$("#myPanelEchartsTask").attr("class", "panel panel-success");
} else if (optionTaskValue < 80) {
$("#myPanelEchartsTask").attr("class", "panel panel-info");
} else {
$("#myPanelEchartsTask").attr("class", "panel panel-danger");
}
},
error:function () {
clearInterval(showInterval);
$("#serviceError").show();
}
});
}
</script>
</body>
</html>
- 编写后端接口ThreadPoolMonitorController.java:
@Controller
@RequestMapping("/threadPoolMonitor")
public class ThreadPoolMonitorController extends BaseController {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(ThreadPoolMonitorController.class);
@Autowired
private IThreadPoolMonitorService threadPoolMonitorService;
@RequestMapping(value = "/show.do")
@ResponseBody
public DataResponse show(){
try{
DataResponse rst = this.threadPoolMonitorService.show();
return rst;
}catch(Exception e){
this.logger.error(e);
return new DataResponse(false,e.getMessage());
}
}
@RequestMapping(value = "/init.do")
@ResponseBody
public DataResponse init(){
this.threadPoolMonitorService.init();
return new DataResponse();
}
@RequestMapping(value = "/destroy.do")
@ResponseBody
public DataResponse destroy(){
this.threadPoolMonitorService.destroy();
return new DataResponse();
}
}
最后页面如图: