java多线程处理 自定义线程池(三)
一、自定义线程池
我们当然不可能每次要新建线程时都去new 一个线程池出来,所以可以写一个自定义的线程池。
看着ThreadPoolExecutor类中的源码来传参数
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @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.
* @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} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
写一个自定义配置类,依次传入
核心线程数:corePoolSize
最大线程数:maximumPoolSize
线程存活时间:keepAliveTime
时间单位:unit
线程队列:workQueue
还要定义一个长100的队列也传进去:
LinkedBlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(100);
多说无益,直接上代码:
package com.springboot.config;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ThreadPoolConfig {
@Bean("main")
public ThreadPoolExecutor mainThreadPoolExecutor() {
LinkedBlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(100);
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(2, 100, 10, TimeUnit.MINUTES, queue);
return threadPoolExecutor;
}
}
上面代码中定义核心线程数2,队列大小为100,存活时间为10分钟。
1、这个自定义线程池怎么用呢?
在springboot项目中,先在类中注入自己配置的线程池。
我们新建的线程池有个名字 main ,在注入时用Qualifier注解 指定出来,可以方便我们自定义多个线程池。
@Autowired
@Qualifier("main")
ThreadPoolExecutor threadPoolExecutor;
直接写一个 test 接口来测试,其中BaseResponse是自己定义的返回类型,自己随便写。
我比较懒,就都直接写Controller中了。
在for循环中新建10个线程,每个睡五秒,然后用submit方法提交给线程池。
@GetMapping("/test")
@ResponseBody
public BaseResponse test() {
System.out.println("主线程开始。。。");
for(int i=0;i<10;i++) {
Thread thread=new Thread(()-> {
System.out.println("当前线程开始"+Thread.currentThread());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread());
});
threadPoolExecutor.submit(thread);
}
System.out.println("主线程结束。。。");
return BaseResponse.success();
}
控制台:
主线程开始。。。
当前线程开始Thread[pool-8-thread-1,5,main]
当前线程开始Thread[pool-8-thread-2,5,main]
主线程结束。。。
当前线程结束Thread[pool-8-thread-2,5,main]
当前线程结束Thread[pool-8-thread-1,5,main]
当前线程开始Thread[pool-8-thread-1,5,main]
当前线程开始Thread[pool-8-thread-2,5,main]
当前线程结束Thread[pool-8-thread-1,5,main]
当前线程开始Thread[pool-8-thread-1,5,main]
当前线程结束Thread[pool-8-thread-2,5,main]
当前线程开始Thread[pool-8-thread-2,5,main]
当前线程结束Thread[pool-8-thread-1,5,main]
当前线程结束Thread[pool-8-thread-2,5,main]
当前线程开始Thread[pool-8-thread-2,5,main]
当前线程开始Thread[pool-8-thread-1,5,main]
当前线程结束Thread[pool-8-thread-2,5,main]
当前线程开始Thread[pool-8-thread-2,5,main]
当前线程结束Thread[pool-8-thread-1,5,main]
当前线程开始Thread[pool-8-thread-1,5,main]
当前线程结束Thread[pool-8-thread-1,5,main]
当前线程结束Thread[pool-8-thread-2,5,main]
由运行结果看出,每次开启两个线程,执行完了再新建线程。可见这一波配置起了作用。
2、有返回值的写法
用CompletableFuture还可以实现异步编排。
@GetMapping("/future")
@ResponseBody
public BaseResponse future() throws InterruptedException, ExecutionException {
System.out.println("主线程开始。。。");
CompletableFuture<Integer> future=CompletableFuture.supplyAsync(()->{
System.out.println("当前线程开始"+Thread.currentThread());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int j=10;
System.out.println("当前线程结束"+Thread.currentThread());
return j;
},threadPoolExecutor);
System.out.println("主线程结束。。。");
System.out.println("返回值:"+future.get());
return BaseResponse.success();
}
控制台:
主线程开始。。。
主线程结束。。。
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-1,5,main]
返回值:10
二、自定义线程池(可配置)
1、让这个自定义线程池看起来专业一点
先写一个参数配置参数类。
可以使用Data注解,这样就不用自己写get,set 方法了。
package com.springboot.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
@Data
@Configuration
@ConfigurationProperties(prefix="threadpool")
public class PoolProperties {
private Integer corePoolSize;
private Integer maximumPoolSize;
private Integer queueSize;
}
将配置类映射到yml配置文件。
threadpool:
corePoolSize: 2
maximumPoolSize: 100
queueSize: 100
将配置类注入到线程池中获取配置的值。
package com.springboot.config;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ThreadPoolConfig {
@Bean("main")
public ThreadPoolExecutor mainThreadPoolExecutor(PoolProperties poolProperties) {
LinkedBlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(poolProperties.getQueueSize());
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(poolProperties.getCorePoolSize()
, poolProperties.getMaximumPoolSize(), 10, TimeUnit.MINUTES, queue);
return threadPoolExecutor;
}
}
重新运行最开始的test接口。
最后的运行结果如下,可见配置生效了。
主线程开始。。。
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程开始Thread[pool-2-thread-2,5,main]
主线程结束。。。
当前线程结束Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-2,5,main]
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程开始Thread[pool-2-thread-2,5,main]
当前线程结束Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-2,5,main]
当前线程开始Thread[pool-2-thread-2,5,main]
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-2,5,main]
当前线程结束Thread[pool-2-thread-1,5,main]
当前线程开始Thread[pool-2-thread-2,5,main]
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-2,5,main]
当前线程开始Thread[pool-2-thread-1,5,main]
当前线程开始Thread[pool-2-thread-2,5,main]
当前线程结束Thread[pool-2-thread-1,5,main]
当前线程结束Thread[pool-2-thread-2,5,main]
三、线程池的监控
直接在springboot项目中写一个接口来监控线程状态。
@GetMapping("/status")
@ResponseBody
public BaseResponse status() {
Map<String, Object> map=new HashMap<String, Object>();
map.put("CorePoolSize", threadPoolExecutor.getCorePoolSize());
map.put("MaximumPoolSize", threadPoolExecutor.getMaximumPoolSize());
map.put("ActiveCount", threadPoolExecutor.getActiveCount());
return BaseResponse.successData(map);
}
浏览器返回结果
{“result”:“SUCCESS”,“msgCode”:“200”,“msgContent”:“操作成功!”,“exceptionContent”:null,“data”:{“MaximumPoolSize”:100,“ActiveCount”:0,“CorePoolSize”:2}}
可以看到最大线程数,活跃线程数,核心线程数被获取到了,值也是我们刚才设置的。
1、两个自定义线程池
当然我们可以定义多个线程池,只要起好名字就好了。
在刚才的参数配置类和配置文件中加上从线程池的配置。
@Data
@Configuration
@ConfigurationProperties(prefix="threadpool")
public class PoolProperties {
private Integer corePoolSize;
private Integer maximumPoolSize;
private Integer queueSize;
private Integer slaveCorePoolSize;
private Integer slaveMaximumPoolSize;
private Integer slaveQueueSize;
}
让参数值不一样以便观察测试效果。
threadpool:
#主线程池
corePoolSize: 2
maximumPoolSize: 100
queueSize: 500
#从线程池
slaveCorePoolSize: 6
slaveMaximumPoolSize: 150
slaveQueueSize: 1000
线程池类中加上从线程池的bean
package com.springboot.config;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ThreadPoolConfig {
@Bean("main")
public ThreadPoolExecutor mainThreadPoolExecutor(PoolProperties poolProperties) {
LinkedBlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(poolProperties.getQueueSize());
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(poolProperties.getCorePoolSize()
, poolProperties.getMaximumPoolSize(), 10, TimeUnit.MINUTES, queue);
return threadPoolExecutor;
}
@Bean("slave")
public ThreadPoolExecutor slaveThreadPoolExecutor(PoolProperties poolProperties) {
LinkedBlockingQueue<Runnable> queue=new LinkedBlockingQueue<>(poolProperties.getSlaveQueueSize());
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(poolProperties.getSlaveCorePoolSize()
, poolProperties.getSlaveMaximumPoolSize(), 10, TimeUnit.MINUTES, queue);
return threadPoolExecutor;
}
}
在Controller类中注入:
@Autowired
@Qualifier("main")
ThreadPoolExecutor threadPoolExecutor;
@Autowired
@Qualifier("slave")
ThreadPoolExecutor slaveThreadPoolExecutor;
把从线程池的监控数据也用刚才那个监控接口返回出来。
@GetMapping("/status")
@ResponseBody
public BaseResponse status() {
Map<String, Object> map=new HashMap<String, Object>();
map.put("CorePoolSize", threadPoolExecutor.getCorePoolSize());
map.put("MaximumPoolSize", threadPoolExecutor.getMaximumPoolSize());
map.put("ActiveCount", threadPoolExecutor.getActiveCount());
map.put("slaveCorePoolSize", slaveThreadPoolExecutor.getCorePoolSize());
map.put("slaveMaximumPoolSize", slaveThreadPoolExecutor.getMaximumPoolSize());
map.put("slaveActiveCount", slaveThreadPoolExecutor.getActiveCount());
return BaseResponse.successData(map);
}
返回结果:
{“result”:“SUCCESS”,“msgCode”:“200”,“msgContent”:“操作成功!”,“exceptionContent”:null,“data”:{“MaximumPoolSize”:100,“ActiveCount”:0,“slaveCorePoolSize”:6,“slaveMaximumPoolSize”:150,“CorePoolSize”:2,“slaveActiveCount”:0}}
可以看到返回了刚才设置的参数,可见配置已经起作用了。
2、线程池监控扩展
自定义线程池不光可以“监” ,还可以“控”。
如在计算机资源吃紧的时候关掉从线程池,放弃一些不重要的业务逻辑。
slaveThreadPoolExecutor.shutdown();
类似的操作方法还有很多,就不一一列举了。