java线程池底层原理(ThreadPoolExecutor)

线程池的存在就是为了合理的利用线程,减少不必要的线程频繁创建销毁,提升程序性能。

在最初接触线程池的时候,我们都知道Executors工具类,可以提供创建不同类型的线程池,包括提供单个线程的线程池、固定数量线程的线程池以及缓存线程池等;

public static ExecutorService newFixedThreadPool(int nThreads){}
public static ExecutorService newSingleThreadExecutor(){}
public static ExecutorService newCachedThreadPool(){}

而这些工具创建线程池,最终都是创建了一个ThreadPoolExecutor对象,该对象提供了多个重载的构造器,通过搭配不同参数实现上述的线程池需求。

ThreadPoolExecutor对象虽然提供了多个重载的构造器,但最终都是在调用同一个构造器:

public ThreadPoolExecutor(int corePoolSize, 
                              int maximumPoolSize, 
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

其中各个参数的意义:

corePoolSize:核心线程数,当线程池待执行任务队列没满时,线程池最多提供的线程数;

maximumPoolSize:最大线程数,当线程池待执行任务队列已满时,会尝试创建更多线程来处理任务,线程总数上限就是maximumPoolSize;

keepAliveTime:如果线程池中的线程数超过了核心线程数,则如果有空闲时间超过keepAliveTime的线程,就会被回收;

unit:keepAliveTime的时间单位;

workQueue:待执行的任务队列,当线程池繁忙且无法创建更多核心线程来处理任务时,任务会被添加到待执行队列,排队等候被执行;

threadFactory:线程工厂,默认是Executors工具类中的静态内部类DefaultThreadFactory,它实现了ThreadFactory接口,该接口只定义了一个方法:

Thread newThread(Runnable r);

DefaultThreadFactory实现代码如下,总结起来就是统一创建同一优先级的非守护线程:

public Thread newThread(Runnable r) {
	Thread t = new Thread(group, r,
						  namePrefix + threadNumber.getAndIncrement(),
						  0);
	if (t.isDaemon())
		t.setDaemon(false);
	if (t.getPriority() != Thread.NORM_PRIORITY)
		t.setPriority(Thread.NORM_PRIORITY);
	return t;
}

handler:拒绝策略,当线程池没有额外的线程处理新的任务,且待执行队列也满了的时候,或者干脆线程池已经被关闭,则新的任务就只能被拒绝,RejectedExecutionHandler是一个接口,该接口定义了如下方法:

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

ThreadPoolExecutor类中提供了几个默认的实现了RejectedExecutionHandler的静态内部类,每个拒绝策略都有自己的特点,具体包括如下几种拒绝策略:

AbortPolicy:简单粗暴型,直接叫你滚,抛出异常,这也是默认的拒绝策略:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	throw new RejectedExecutionException("Task " + r.toString() +
										 " rejected from " +
										 e.toString());
}

CallerRunsPolicy:兢兢业业型,你没空做,那我自己做:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	if (!e.isShutdown()) {
		r.run();
	}
}

DiscardPolicy:高冷型,完全不搭理你:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

DiscardOldestPolicy:喜新厌旧型,丢弃掉待执行队列中最老的任务,添加新的任务:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	if (!e.isShutdown()) {
		e.getQueue().poll();
		e.execute(r);
	}
}

如果以上几种类型你都不喜欢,那么你可以自己实现RejectedExecutionHandler接口,口味自调。

清楚了以上的参数,那使用线程池就很简单了,一个很简单的例子如下:

public class ThreadPoolTest {
	
	private static ThreadPoolExecutor pool = new ThreadPoolExecutor(
			1, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), 
			Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
	
	public static void main(String[] args) {
		pool.execute(()->{
			System.out.println("task execute");
		});
		pool.shutdown();
	}

}

关于上面这个简单的案例,我们分三步看:

1、线程池的创建,这个就是利用ThreadPoolExecutor的构造器实例化对象,参数在上面都已经详细介绍了,此处没什么再可说的了;

2、execute提交任务,总体逻辑如下图所示:

 3、shutdown,关闭线程池,说到这里,要说下线程池的几个状态了

RUNNING:线程池创建后即为该状态,可以执行队列中的任务也可以接收并处理新的任务;

SHUTDOWN:当调用shutdown()方法后,线程池进入该状态,此状态下线程池不再接收新的任务,如果继续提交任务,则执行拒绝策略,但是会继续执行队列中的任务;

STOP:当调用shutdownNow()方法后,线程池进入该状态,此状态下线程池不再接收新的任务,也不再处理队列中的任务,而且会尝试中断执行中的任务;

TIDYING:当线程池中所有任务都结束,且线程也全部被回收,则进入TIDYING状态;

TERMINATED:terminated()方法执行后,线程池彻底终结;

线程池中的线程是如何启动并回收的呢?

线程池中的每个线程是对应一个Worker实例,在创建完后,会循环处理提交的任务,在上文中的图中有所体现:

Worker的run()方法调用了ThreadPoolExecutor的runWoker方法:

public void run() {
	runWorker(this);
}

runWorker源码如下:

final void runWorker(Worker w) {
	Thread wt = Thread.currentThread();
	Runnable task = w.firstTask;
	w.firstTask = null;
	w.unlock(); // allow interrupts
	boolean completedAbruptly = true;
	try {
		while (task != null || (task = getTask()) != null) {
			w.lock();
			// If pool is stopping, ensure thread is interrupted;
			// if not, ensure thread is not interrupted.  This
			// requires a recheck in second case to deal with
			// shutdownNow race while clearing interrupt
			if ((runStateAtLeast(ctl.get(), STOP) ||
				 (Thread.interrupted() &&
				  runStateAtLeast(ctl.get(), STOP))) &&
				!wt.isInterrupted())
				wt.interrupt();
			try {
				beforeExecute(wt, task);
				Throwable thrown = null;
				try {
					task.run();
				} catch (RuntimeException x) {
					thrown = x; throw x;
				} catch (Error x) {
					thrown = x; throw x;
				} catch (Throwable x) {
					thrown = x; throw new Error(x);
				} finally {
					afterExecute(task, thrown);
				}
			} finally {
				task = null;
				w.completedTasks++;
				w.unlock();
			}
		}
		completedAbruptly = false;
	} finally {
		processWorkerExit(w, completedAbruptly);
	}
}

runWorker中用了个while循环处理任务,只要while循环不退出,则当前Worker线程就一直运行,从源码中可以看出,退出条件是task为null,也就是getTask()这个方法是控制Worker生命周期的关键方法,其源码如下:

private Runnable getTask() {
	boolean timedOut = false; // Did the last poll() time out?

	for (;;) {
		int c = ctl.get();
		int rs = runStateOf(c);

		// Check if queue empty only if necessary.
		if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
			decrementWorkerCount();
			return null;
		}

		int wc = workerCountOf(c);

		// Are workers subject to culling?
		boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

		if ((wc > maximumPoolSize || (timed && timedOut))
			&& (wc > 1 || workQueue.isEmpty())) {
			if (compareAndDecrementWorkerCount(c))
				return null;
			continue;
		}

		try {
			Runnable r = timed ?
				workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
				workQueue.take();
			if (r != null)
				return r;
			timedOut = true;
		} catch (InterruptedException retry) {
			timedOut = false;
		}
	}
}

1、正常情况下,会采用阻塞的方式(即take()方法)从待执行队列获取任务,即便被interrupt,也会再次循环阻塞获取,这样必然能保证返回可执行的任务,则worker线程不会被回收;

2、线程池如果进入SHUTDOWN状态,且待执行队列中的数据也被消费完了,则返回null,这样就保证了所有的worker线程会结束运行,从而被回收;

3、线程池如果进入了STOP状态,则不管待执行队列有没有任务,都会立即返回null,则worker线程结束;

4、如果当前worker线程总数超过了指定的核心线程数,则从队列获取数据时,使用超时阻塞的方式(即poll(long timeout, TimeUnit unit)方法)获取,超时时间即keepAliveTime,超时后如果没获取到任务,则线程空闲时间达到阈值,返回null,结束worker线程;

5、如果配置了了核心线程数也需要超时回收,则获取任务时同样采取的是超时阻塞方式,同第4点;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值