Java 自定义线程池 详解使用和优化策略

什么是线程池?为什么要用线程池?  

  1. 降低资源的消耗。降低线程创建和销毁的资源消耗;
  2. 提高响应速度:线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间
  3. 提高线程的可管理性。

JDK中的线程池和工作机制

线程池的创建

ThreadPoolExecutor,jdk所有线程池实现的父类

 

各个参数含义

int corePoolSize  :线程池中核心线程数,< corePoolSize  ,就会创建新线程,= corePoolSize  ,这个任务就会保存到BlockingQueue,如果调用prestartAllCoreThreads()方法就会一次性的启动corePoolSize  个数的线程。

int maximumPoolSize:允许的最大线程数,BlockingQueue也满了,< maximumPoolSize时候就会再次创建新的线程

long keepAliveTime: 线程空闲下来后,存活的时间,这个参数只在> corePoolSize才有用

TimeUnit unit:存活时间的单位值

BlockingQueue<Runnable> workQueue, 保存任务的阻塞队列

ThreadFactory threadFactory, 创建线程的工厂,给新建的线程赋予名字

RejectedExecutionHandler handler :饱和策略

             AbortPolicy :直接抛出异常,默认;

            CallerRunsPolicy:用调用者所在的线程来执行任务

            DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务

           DiscardPolicy :当前任务直接丢弃

实现自己的饱和策略,实现RejectedExecutionHandler接口即可(一般线上都会使用自定义的饱和策略)

 

提交任务

execute(Runnable command)  不需要返回

Future<T> submit(Callable<T> task) 需要返回

关闭线程池

shutdown(),shutdownNow();

shutdownNow():设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程

shutdown()设置线程池的状态,只会中断所有没有执行任务的线程

 

线程池的工作原理

1、首先判断线程池中核心线程池(corePoolSize)是否已经满了,如果不是,则丢到工作队列(BlockingQueue)里。

2、判断工作队列(BlockingQueue)是否满了,如果没有满,将线程放入队列。否则创建新的线程来执行任务。

3、 如果创建一个新的工作线程数量和当前运行的线程数量超过maximumPoolSize,则交给RejectedExecutionHandler淘汰策略来处理。

 

合理配置线程池

根据任务的性质来:计算密集型(CPU),IO密集型,混合型

计算密集型:加密,大数分解,正则……., 线程数适当小一点,最大推荐:机器的Cpu核心数+1,为什么+1,防止页缺失,(机器的Cpu核心=Runtime.getRuntime().availableProcessors();)

IO密集型:读取文件,数据库连接,网络通讯, 线程数适当大一点,机器的Cpu核心数*2,

混合型:尽量拆分,IO密集型>>计算密集型,拆分意义不大,IO密集型~计算密集型

队列的选择上,应该使用有界,无界队列可能会导致内存溢出,OOM

 

 

预定义的线程池

FixedThreadPool

创建固定线程数量的,适用于负载较重的服务器,使用了无界队列

SingleThreadExecutor

创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用了无界队列

CachedThreadPool

会根据需要来创建新线程的,执行很多短期异步任务的程序,使用了SynchronousQueue

WorkStealingPool(JDK7以后)

基于ForkJoinPool实现

ScheduledThreadPoolExecutor

需要定期执行周期任务,Timer不建议使用了。

newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务

newScheduledThreadPool 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候

方法说明:

schedule:只执行一次,任务还可以延时执行

scheduleAtFixedRate:提交固定时间间隔的任务

scheduleWithFixedDelay:提交固定延时间隔执行的任务

两者的区别:

 

 

scheduleAtFixedRate任务超时:

规定60s执行一次,有任务执行了80S,下个任务马上开始执行

第一个任务 时长 80s,第二个任务20s,第三个任务 50s

第一个任务第0秒开始,第80S结束;

第二个任务第80s开始,在第100秒结束;

第三个任务第120s秒开始,170秒结束

第四个任务从180s开始

建议在提交给ScheduledThreadPoolExecutor的任务要住catch异常。

 

代码实现

 创建一个任务执行类

/**
 * 任务执行类
 */
class Worker implements Runnable{
	// 任务编号
	private String id;
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	public Worker(String id) {
		super();
		this.id = id;
	}

	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println(id + " 正常执行!");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
}

 自己实现的线程池饱和策略

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * 自己实现的线程池饱和策略
 * @author James Lee
 *
 */
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {

	// 具体实现方法,一般上线后,会把多余的线程任务,保存到日志或写库,这里模拟尝试几种处理
	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		try {
			Worker worker = (Worker)r;
			// 模拟写日志
			System.out.println(worker.getId()+" 被丢弃!");
			// 重新放入队列里执行
			executor.getQueue().put(r);
		
		} catch (InterruptedException e) {
			// 异常发生后打印
			System.out.println("线程进入饱和策略发生异常:线程信息:" + r.toString());
		}
	}

}

测试

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExecutorsDemo {
	// 核心线程数(为了模拟看效果设置小一点)
	private static final int corePoolSize = 2;
	// 最大线程数(为了模拟看效果设置小一点)
	private static final int maximumPoolSize = 5;
	// 线程空闲下来后,存活的时间(ms)
	private static final long keepAliveTime = 0L;
	// 自定义线程池
	public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
			corePoolSize, 
			maximumPoolSize, 
			keepAliveTime, 
			TimeUnit.MICROSECONDS, // 单位毫秒
			new ArrayBlockingQueue<Runnable>(3),// 任务队列最多存放3个任务
			new MyRejectedExecutionHandler());
	

	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i <=20; i++) {
			pool.execute(new Worker("worker:"+i));
		}
		// 归还线程池
		pool.shutdown();
	}

}

测试结果及说明

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值