java并发之线程池

简介:池化技术相比大家大家已经屡见不鲜了,线程池、数据库连接池、Http连接池等等都是对这个思想的应用。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池类的相关结构
在这里插入图片描述
ExecutorService接口继承了Executor接口,定义了管理线程任务的方法。ExecutorService的抽象类AbstractExecutorService提供了submit、invokeAll()等部分方法实现,但是核心方法Executor.execute()并没有实现。因为所有任务都在这个方法里执行,不同的线程池实现策略会有不同,所以交由具体的线程池来实现。
如何创建线程池
在JDK中提供了两种创建线程池的方法,使用Executors的静态工厂方法去创建和通过真正的线程池类是ThreadPoolExecutor显示的去创建自己想要的线程池。ThreadPoolExecutor构造方法如下:
在这里插入图片描述
参数解释
1、 corePoolSize:核心线程数。如果等于0,则任务执行完后,没有任务请求进入时销毁线程池中的线程。如果大于0,即使本地任务执行完毕,核心线程也不会被销毁。设置过大会浪费系统资源,设置过小导致线程频繁创建。
2、maximumPoolSize:最大线程数。必须大于等于1,且大于等于corePoolSize。如果与corePoolSize相等,则线程池大小固定。如果大于corePoolSize,则最多创建maximumPoolSize个线程执行任务,其他任务加入到workQueue缓存队列中,当workQueue为空且执行任务数小于maximumPoolSize时,线程空闲时间超过keepAliveTime会被回收。
3、keepAliveTime:线程空闲时间。线程池中线程空闲时间达到keepAliveTime值时,线程会被销毁,只到剩下corePoolSize个线程为止。默认情况下,线程池的最大线程数大于corePoolSize时,keepAliveTime才会起作用。如果allowCoreThreadTimeOut被设置为true,即使线程池的最大线程数等于corePoolSize,keepAliveTime也会起作用(回收超时的核心线程)。
4、unit:TimeUnit表示时间单位。
5、workQueue:缓存队列。当请求线程数大于maximumPoolSize时,线程进入BlockingQueue阻塞队列。
6、threadFactory:线程工厂。用来生产一组相同任务的线程。主要用于设置生成的线程名词前缀、是否为守护线程以及优先级等。设置有意义的名称前缀有利于在进行虚拟机分析时,知道线程是由哪个线程工厂创建的。
7、handler:执行饱和策略对象。当达到任务缓存上限时(即超过workQueue参数能存储的任务数),执行饱和策略,可以看作简单的限流保护。关于饱和策略下面单独介绍下
ThreadPoolExecutor饱和策略

  • ThreadPoolExecutor.AbortPolicy:默认,抛出RejectedExecutionExceotion来拒绝新任务的处理
  • ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务。你不会任务请求。但是这种策略会降低对于新任务提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果你的应用程序可以承受延迟并且你不能丢弃任何一个任务请求的话,你可以选择这个策略。
  • ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉。
  • ThreadPoolExecutor.DiscardOldestPolicy:此策略将丢弃最早的未处理的任务请求

Executors类提供了4种不同的线程池
1、newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成cpu过度切换)

2、newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)

3、newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。

4、newScheduledThreadPool:适用于执行延时或者周期性任务。

弊端
newFixedThreadPool和newSingleThreadExecutor:
允许请求的队列长度为Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
newCachedThreadPoo和newScheduledThreadPool:
允许请求的线程数量为Integer.MAX_VALUE,可能创建大量线程,从而导致OOM。
所以《阿里巴巴开发手册中》强制线程池不允许使用Execotors去创建,而是通过ThreadPoolExecutor的方式,这样开发者更清楚自己的线程规则,避免资源耗尽的风险。

线程池流程
在这里插入图片描述
为了更好的理解上图,下面通过代码模拟线程池每次同时会执行5个任务,5个任务结束后,剩余的5个才会被执行。
创建Runnable接口实现类:

public class MyRunnable implements Runnable{
	private String command;

	public MyRunnable(String s) {
		this.command = s;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + " Start. Time =" + new Date());
		processCommand();
		System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());

	}

	private void processCommand() {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Override
	public String toString() {
		return this.command;
	}
}

使用ThreadPoolExecutor构造函数创建线程池:

public class ThreadPoolExecutorDemo {
	private static final int CORE_POOL_SIZE = 5;
	private static final int MAX_POOL_SIZE = 10;
	private static final int QUEUE_CAPACITY = 100;
	private static final Long KEEP_ALIVE_TIME = 1L;

	public static void main(String[] args) {
		// 使⽤阿⾥巴巴推荐的创建线程池的⽅式
		// 通过ThreadPoolExecutor构造函数⾃定义参数创建
		ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME,
				TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy());
		for (int i = 0; i < 10; i++) {
			// 创建WorkerThread对象(WorkerThread类实现了Runnable 接⼝)
			Runnable worker = new MyRunnable("" + i);
			// 执⾏Runnable
			executor.execute(worker);
		}
		// 终⽌线程池
		executor.shutdown();

		while (!executor.isTerminated()) {
		}
		System.out.println("Finished all threads");
	}
}

结果:
在这里插入图片描述
总结线程池的好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果是无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值