并发编程(十六):线程池

一,ThreadPoolExecutor 概述

1,线程池优势

    在Java中,如果每个请求到达就创建一个线程,创建线程和销毁线程对系统资源的消耗都非常大,甚至可能比实际业务处理消耗的资源都大。同时,如果在JVM中创建太多的线程,也可能由于过度消耗内存或调度切换从而导致系统资源不足。

    为了解决上面提出的问题,就有了线程池的概念。线程池,就是在一个线程容器中提前放置一定量的初始化线程,如果业务需要创建线程进行业务处理,则直接从线程池中获取一个线程进行执行,执行完成后归还线程到线程池,同时线程池也会对创建线程进行限制,不会无休止的创建下去。

    使用线程池后,可以后下面几点优势:

        * 减低创建线程和销毁线程的开销

        * 提高响应速度,当有新任务需要执行时不需要等待线程创建时就可以直接执行(如果有空闲线程)

        * 合理的设置线程池的大小可以避免因为硬件瓶颈带来的性能问题

2,类图

3,线程池常用API

// 线程池初始化
public ThreadPoolExecutor(
	// 核心线程数
	int corePoolSize,
	// 最大线程数
	int maximumPoolSize,
	// 线程空闲保留时间
	long keepAliveTime,
	// 线程保留单位
	TimeUnit unit,
	// 线程任务阻塞容器
	BlockingQueue<Runnable> workQueue,
	// 线程工厂,一般取默认
	ThreadFactory threadFactory,
	// 拒绝策略
	RejectedExecutionHandler handler);
	
// 线程执行,不带返回值
public void execute(Runnable command);

// 线程执行,带返回值
public <T> Future<T> submit(Callable<T> task);
public <T> Future<T> submit(Runnable task, T result);
public Future<?> submit(Runnable task);

// 关闭线程池
public void shutdown();

4,线程池常量解析,分为线程池常量和Future常量两部分

/********************* 线程池常量 **********************/
// 数量为,该值是29
// 线程池把 Integer 的32位拆分为高3位和低29位,
// 通过高三位表示状态,低29位表示正在执行的线程数量,
private static final int COUNT_BITS = Integer.SIZE - 3;

// 最大允许执行的线程数量,因为高三位表示状态,所以天然限制为29位
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 线程池运行状态
// -1 的二进制是 11111111 11111111 11111111 11111111
// 右移29位就是  11100000 00000000 00000000 00000000
// 高三位表示状态,所以 RUNNING 的状态就是 111
private static final int RUNNING    = -1 << COUNT_BITS;

// 线程池关闭状态,不接收新任务,但是执行队列中的人物
// 状态为 0
private static final int SHUTDOWN   =  0 << COUNT_BITS;

// 线程池停止状态,不接受新任务,不执行队列人物,终止执行任务
// 状态为 1
private static final int STOP       =  1 << COUNT_BITS;

// 所有任务都已经结束,线程数量为0,处于该状态的线程池即将调用 terminated()方法
// 状态为 10
private static final int TIDYING    =  2 << COUNT_BITS;

// terminated()方法执行完成
// 状态为 11
private static final int TERMINATED =  3 << COUNT_BITS;

/********************* Future常量 **********************/
// Future 常量主要是对 state 状态的几种情况分析
// NEW 新建状态,表示这个 FutureTask还没有开始运行
private static final int NEW = 0; 

// COMPLETING 完成状态,表示 FutureTask 任务已经计算完毕了
// 但是还有一些后续操作,例如唤醒等待线程操作,还没有完成。
private static final int COMPLETING = 1;

// FutureTask 任务完结,正常完成,没有发生异常
private static final int NORMAL = 2;

// FutureTask 任务完结,因为发生异常。
private static final int EXCEPTIONAL = 3;

// FutureTask 任务完结,因为取消任务
private static final int CANCELLED = 4;

// FutureTask 任务完结,也是取消任务,不过发起了中断运行任务线程的中断请求
private static final int INTERRUPTING = 5;

// FutureTask 任务完结,也是取消任务,已经完成了中断运行任务线程的中断请求
private static final int INTERRUPTED = 6;

5,JDK提供的几种常用线程池,阿里开发手册不提倡用内置的线程池,建议通过构造器自行初始化,后续自行构造的执行流程会分析,该部分参考即可;跟源码可以发下,各种初始化方式最终也是通过构造器初始化!

// 初始化定长线程池
// 内置指定的核心线程数和最大线程数,并按照线程池流程执行
public static ExecutorService newFixedThreadPool(int nThreads);

// 初始化缓存的线程池
// 没有核心线程数,默认最大线程数为 Integer.MAX_VALUE,阻塞队列为 SynchronousQueue,不保存数据
// 所有对于缓存的线程池来说,接收一个任务的同时就需要执行一个任务
public static ExecutorService newCachedThreadPool();

// 初始化单例的线程池
// 一次最多执行一个线程任务,多线程竞争时,添加到阻塞队列等候
public static ExecutorService newSingleThreadExecutor();

// 初始化定时的线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);

6,功能DEMO

package com.gupao.concurrent;

import java.util.concurrent.*;

/**
 * @author pj_zhang
 * @create 2019-10-31 21:56
 **/
public class ThreadPoolTest {

    private static ThreadPoolExecutor executor =
            new ThreadPoolExecutor(20, 20,
                    0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("THREAD 线程执行");
        });

        Runnable runnable = () -> {
            System.out.println("RUNNABLE 线程执行");
        };

        Callable<String> callable = () -> {
            Thread.sleep(2000);
            return "Callable 线程执行";
        };

        executor.execute(thread);
        executor.execute(runnable);
        // 此处打印时间,是为了演示 Future 的阻塞获取结果
        System.out.println("callable执行前:" + System.currentTimeMillis());
        Future<String> future = executor.submit(callable);
        String callableResult = future.get();
        System.out.println("callable执行后:" + System.currentTimeMillis() + ", " + callableResult);
    }

}

二,源码分析

1,底层方法分析

1.1,ctlOf(int rs, int wc):获取线程池状态+数量的 Integer 值,

private static int ctlOf(int rs, int wc) { 
	return rs | wc; 
}

1.2,workerCountOf(int c):获取工作线程数量

private static int workerCountOf(int c)  {
	// 根据 ctlOf 获取到的值,用低29位进行与运算,获取到线程数量
	return c & CAPACITY; 
}

1.3,runStateOf(int c):获取线程状态

private static int runStateOf(int c)     { 
    return c & ~CAPACITY; 
}

2,ThreadPoolExecutor 初始化及执行流程

2.1,ThreadPoolExecutor()

public ThreadPoolExecutor(int corePoolSize,
						  int maximumPoolSize,
						  long keepAliveTime,
						  TimeUnit unit,
						  BlockingQueue<Runnable> workQueue,
						  ThreadFactory threadFactory,
						  RejectedExecutionHandler handler) {
	if (corePoolSize < 0 ||
		maximumPoolSize <= 0 ||
		maximumPoolSize < corePoolSize ||
		keepAliveTime < 0)
		throw new IllegalArgumentException();
	if (workQueue == null || threadFactory == null || handler == null)
		throw new NullPointerException();
	this.acc = System.getSecurityManager() == null ?
			null :
			AccessController.getContext();
	// 核心线程数
	this.corePoolSize = corePoolSize;
	// 最大线程数
	this.maximumPoolSize = maximumPoolSize;
	// 阻塞队列
	this.workQueue = workQueue;
	// 保持存活时间
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	// 线程工厂,这个直接取默认
	this.threadFactory = threadFactory;
	// 拒绝策略
	this.handler = handler;
}

2.2,线程池执行流程

    * 接收到线程任务后,首先判断核心线程有没有被全部占用;没有被全部占用,随机构建一个线程执行任务

    * 如果核心线程全部被占用,查看阻塞队列是否已满;如果没有满,添加到阻塞队列

    * 如果阻塞队列已满,继续看最大线程数有没有被全部占用;如果存在空闲,构建线程执行任务

    * 如果最大线程数已经全部占用,根据定义的拒绝策略进行拒绝操作

2.3,线程池拒绝策略

    * 线程池提供了四种拒绝策略,分别是 RejectedExecutionHandler 接口的四种实现

// java.util.concurrent.ThreadPoolExecutor.AbortPolicy
// 异常处理
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	throw new RejectedExecutionException("Task " + r.toString() +
										 " rejected from " +
										 e.toString());
}

// java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
// 丢弃最前面的任务,重新添加执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	if (!e.isShutdown()) {
		e.getQueue().poll();
		e.execute(r);
	}
}

// java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
// 什么都不做,即丢弃当前任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

// java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
// 只要线程池没有关闭,则直接开线程运行,该策略建议慎用,不收控制
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	if (!e.isShutdown()) {
		r.run();
	}
}

// 最后,业务可以自定义拒绝方式,只需要实现 RejectedExecutionHandler 接口,然后重写其接口方法

3,execute()

    * execute(Runnable command):刚才对线程池的大概执行流程进行了分析,该方法内可以看出代码实现

public void execute(Runnable command) {
	if (command == null)
		throw new NullPointerException();
	// 获取状态位 + 数量位的 int 对象
	int c = ctl.get();
	// 获取工作线程数量,首先判断核心线程数
	if (workerCountOf(c) < corePoolSize) {
		// 存在空闲的核心线程,进行线程执行
		if (addWorker(command, true))
			return;
		// 如果执行失败,重新对 c 赋值,说明存在线程竞争
		c = ctl.get();
	}
	// isRunning(c):线程池依旧运行状态
	// workQueue.offer(command):添加到队列成功
	// 次数是判断加入到阻塞队列是否成功
	if (isRunning(c) && workQueue.offer(command)) {
		int recheck = ctl.get();
		// 如果线程池不处于运行状态,则从队列中移除当前任务
		// 并执行拒绝策略
		if (! isRunning(recheck) && remove(command))
			reject(command);
		// 如果之前的线程已经被销毁完,新建一个线程
		else if (workerCountOf(recheck) == 0)
			addWorker(null, false);
	}
	// 阻塞队列添加失败,唤醒最大线程执行,
	else if (!addWorker(command, false))
		// 最大线程执行失败,进行拒绝策略处理
		reject(command);
}

    * addWorker(Runnable firstTask, boolean core)

// Runnable firstTask:当前线程任务
// boolean core:是否核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
	retry:
	for (;;) {
		// 获取线程执行状态
		int c = ctl.get();
		// 这部分计算后续搞明白再填充 TODO
		int rs = runStateOf(c);
		// 线程池已经关闭,不再接受新任务
		// SHUTDOWN 状态不接受新任务,但仍然会执行已经加入任务队列的任务
		// 所以当进入 SHUTDOWN 状态,而传进来的任务为空,并且任务队列不为空的时候,是允许添加新线程的,
		// 如果把这个条件取反,就表示不允许添加 worker
		if (rs >= SHUTDOWN &&
			! (rs == SHUTDOWN &&
			   firstTask == null &&
			   ! workQueue.isEmpty()))
			return false;
		// 自旋添加任务
		for (;;) {
			// 获取工作线程
			int wc = workerCountOf(c);
			// 首先判断是否大于最大允许数量
			// 然后根据是否核心线程判断是否大于核心线程数或者最大线程数
			if (wc >= CAPACITY ||
				wc >= (core ? corePoolSize : maximumPoolSize))
				return false;
			// 对c递增,也就是工作线程数递增
			if (compareAndIncrementWorkerCount(c))
				break retry;
			// 递增失败,说明存在线程竞争或者状态变更,继续自旋处理
			c = ctl.get(); 
			// 此处不等于,说明存在线程池状态变更
			// 等于,说明只是存在线程竞争造成的CAS失败
			if (runStateOf(c) != rs)
				continue retry;
		}
	}

	// 上半部分基本是对线程池状态及工作线程数进行判断,并最终对工作线程+1,表示当前线程已经抢占到一个线程位置
	boolean workerStarted = false;
	boolean workerAdded = false;
	Worker w = null;
	try {
		// 包装线程对象为 Worker 对象
		w = new Worker(firstTask);
		// 通过 ThreadFactory 构建一个新的线程
		final Thread t = w.thread;
		if (t != null) {
			// 此处加重入锁
			final ReentrantLock mainLock = this.mainLock;
			mainLock.lock();
			try {
				// 继续获取线程池状态进行判断
				int rs = runStateOf(ctl.get());

				if (rs < SHUTDOWN ||
					(rs == SHUTDOWN && firstTask == null)) {
					// 如果线程已经运行中,则说明存在问题,此处线程还没有启用
					if (t.isAlive())
						throw new IllegalThreadStateException();
					// 添加线程包装后的Worker对象到列表中
					workers.add(w);
					int s = workers.size();
					if (s > largestPoolSize)
						largestPoolSize = s;
					// 表示工作线程创建成功
					workerAdded = true;
				}
			} finally {
				mainLock.unlock();
			}
			if (workerAdded) {
				// 线程启动,
				// 此处注意,Worker 实现了 Runnable接口,则此处是调用 Worker.run()
				t.start();
				workerStarted = true;
			}
		}
	} finally {
		// 如果添加失败,则递减工作线程数
		if (! workerStarted)
			addWorkerFailed(w);
	}
	return workerStarted;
}

    * runWorker(Worker w):Worker的 run() 方法内部调用该方法

final void runWorker(Worker w) {
	Thread wt = Thread.currentThread();
	// 获取初始化 Worker时传递的线程
	// 此处分析的是 execute() 触发,如果是 submit() 触发,此处的Runnable实现类应该为 FutureTask,
	// task.run() 最终调用 FutureTask.run()方法,此处会对 Future的状态进行处理,实现阻塞获取的功能
	Runnable task = w.firstTask;
	w.firstTask = null;
	// unlock,表示当前 worker 线程允许中断,因为 new Worker 默认的 state=-1,
	// 此处是调用Worker 类的 tryRelease()方法,将 state 置为 0, 
	// 而 interruptIfStarted()中只有 state>=0 才允许调用中断
	w.unlock();
	boolean completedAbruptly = true;
	try {
		// 任务不为空,则持续执行
		// getTask():此处表示不断从阻塞队列中获取元素
		while (task != null || (task = getTask()) != null) {
			w.lock();
			// 线程池状态为stop时不接受新任务,并中断正在执行的人物
			// (Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP)确保线程中断标志位为 true 且是 stop 状态以上,接着清除了中断标志
			// !wt.isInterrupted()则再一次检查保证线程需要设置中断标志位
			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() 的task的不同实现
					// 在submit()触发的功能中,表示FutureTask
					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 {
		// 将入参 worker 从数组 workers 里删除掉;
		// 根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组workers
		processWorkerExit(w, completedAbruptly);
	}
}

    * getTask():从阻塞队列中获取下一个有效任务。线程池定义的超时处理再该部分实现

private Runnable getTask() {
	boolean timedOut = false;

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

		// 校验线程池状态
		if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
			decrementWorkerCount();
			return null;
		}

		// 获取工作线程
		int wc = workerCountOf(c);
		// 对超时线程进行时间控制
		// allowCoreThreadTimeOut默认为false,表示核心线程不收控制
		// wc > corePoolSize:超过核心线程,即最大线程数,则触发控制
		boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

		// timedOut为true,说明上次阻塞操作已经超时,则工作线程数-1,并返回null
		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;
		}
	}
}

    * addWorkerFailed(Worker w):添加工作线程失败

private void addWorkerFailed(Worker w) {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		// 从列表中移除当前 Worker
		if (w != null)
			workers.remove(w);
		// 工作线程数递减
		decrementWorkerCount();
		// 尝试修改线程状态为 Terminate
		tryTerminate();
	} finally {
		mainLock.unlock();
	}
}

private void decrementWorkerCount() {
	do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}

    * reject(Runnable command):拒绝策略

final void reject(Runnable command) {
	// 直接执行拒绝策略
	handler.rejectedExecution(command, this);
}

4,submit():同时对 Future 进行分析

    * submit(Callable<T> task):触发线程执行

public <T> Future<T> submit(Callable<T> task) {
	if (task == null) throw new NullPointerException();
	// 初始化化一个 FutureTask,实现了 Runnable 接口
	RunnableFuture<T> ftask = newTaskFor(task);
	// 直接执行 execute 方法
	execute(ftask);
	return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
	return new FutureTask<T>(callable);
}

    * 在 runWorker() 方法中,触发 task.run(),实际调用的是 Future.run()方法

public void run() {
	// 状态不为new,直接执行异常
	if (state != NEW ||
		!UNSAFE.compareAndSwapObject(this, runnerOffset,
									 null, Thread.currentThread()))
		return;
	try {
		Callable<V> c = callable;
		// 线程一切正常,准备执行
		if (c != null && state == NEW) {
			V result;
			boolean ran;
			try {
				// 直接调用Callable的call方法,并返回结果
				result = c.call();
				ran = true;
			} catch (Throwable ex) {
				result = null;
				ran = false;
				setException(ex);
			}
			// 设置结果
			if (ran)
				set(result);
		}
	} finally {
		runner = null;
		int s = state;
		if (s >= INTERRUPTING)
			handlePossibleCancellationInterrupt(s);
	}
}

    * set(V v):设置执行结果

protected void set(V v) {
	// 设置状态为完成
	if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
		outcome = v;
		// 设置正常结束
		UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
		// 完成后续处理,唤醒等待节点
		finishCompletion();
	}
}

    * finishCompletion():该方法主要是唤醒等待节点,去返回结果中拿数据

private void finishCompletion() {
	// 获取等待节点
	for (WaitNode q; (q = waiters) != null;) {
		if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
			for (;;) {
				// 等待节点线程优先,直接唤醒
				Thread t = q.thread;
				if (t != null) {
					q.thread = null;
					LockSupport.unpark(t);
				}
				// 递归下一个等待节点同步处理
				WaitNode next = q.next;
				if (next == null)
					break;
				q.next = null; // unlink to help gc
				q = next;
			}
			break;
		}
	}

	done();

	callable = null;        // to reduce footprint
}

5,Funture.get()

    * get():阻塞获取数据

public V get() throws InterruptedException, ExecutionException {
	// 获取Future对应的线程执行状态,如果没有执行完直接等待
	int s = state;
	if (s <= COMPLETING)
		s = awaitDone(false, 0L);
	// 执行完成后,解析结果集
	return report(s);
}

    * awaitDone(boolean timed, long nanos)

private int awaitDone(boolean timed, long nanos) throws InterruptedException {
	final long deadline = timed ? System.nanoTime() + nanos : 0L;
	WaitNode q = null;
	boolean queued = false;
	for (;;) {
		// 线程中断,直接移除
		if (Thread.interrupted()) {
			removeWaiter(q);
			throw new InterruptedException();
		}

		int s = state;
		// 此处说明执行完成
		if (s > COMPLETING) {
			if (q != null)
				q.thread = null;
			return s;
		}
		// 还有后续操作没有执行完成,暂时让出时间片段,稍后执行
		else if (s == COMPLETING)
			Thread.yield();
		// 表示状态为null,构建等待节点,准备等待
		else if (q == null)
			q = new WaitNode();
		// 使用CAS添加等待节点到等待队列
		else if (!queued)
			queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
												 q.next = waiters, q);
		// 此处表示设置了超时
		else if (timed) {
			// 如果超时时间没有获取到值,则直接退出
			nanos = deadline - System.nanoTime();
			if (nanos <= 0L) {
				removeWaiter(q);
				return state;
			}
			LockSupport.parkNanos(this, nanos);
		}
		else
			// 添加等待队列成功后,线程挂起等待,等待执行完成后进行唤醒,完成那部分已经分析
			LockSupport.park(this);
	}
}

    * report(int s):获取返回值

private V report(int s) throws ExecutionException {
	// 获取返回值
	Object x = outcome;
	if (s == NORMAL)
		return (V)x;
	if (s >= CANCELLED)
		throw new CancellationException();
	throw new ExecutionException((Throwable)x);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值