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();

类似的操作方法还有很多,就不一一列举了。

发布了17 篇原创文章 · 获赞 1 · 访问量 319

猜你喜欢

转载自blog.csdn.net/weixin_43424932/article/details/103969674
今日推荐