Java_11_ThreadPoolExecutor_线程池核心参数_源码解析

Java构建线程的方式

Java中创建线程的方式:

  1. 继承Thread类并重写run方法,实现简单但不可以继承其他。
  2. 实现Runnable接口并重写run方法。避免了单继承局限性,编程更加灵活,实现解耦。
  3. 实现Callable接口并重写call方法。可以获取线程执行结果的返回值,并且可以抛出异常。
  4. 使用线程池创建(使用java.util.concurrent.Executor接口)。

非线程池创建线程

class ThreadOne extends Thread {
    @Override
    public void run() {
        System.out.println("Thread-One is Running");
    }
}

class ThreadTwo implements Runnable{
    @Override
    public void run() {
        System.out.println("Thread-Two is Running");
    }
}

class ThreadThree implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("Thread-Three is Running");
        return 100;
    }
}

public class ThreadCreateTest{
    public static void main(String[] args){
        ThreadOne threadOne = new ThreadOne();
        threadOne.start();

        ThreadTwo threadTwo = new ThreadTwo();
        Thread thread2 = new Thread(threadTwo);
        thread2.start();

        ThreadThree threadThree = new ThreadThree();
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(threadThree);
        Thread thread3 = new Thread(integerFutureTask);
        thread3.start();
        Integer integer = integerFutureTask.get();
        System.out.println("Thread-Three's result is " + integer);
    }
}

Executors工具类

概述:线程池方式其实和上面没有区别,只不过上面需要频繁的创建和销毁线程,会造成一些不必要的额外资源消耗,所以在实际开发中肯定会采用线程池的方式,Java中的Executors自带了一些创建线程池的方式。

在这里插入图片描述

在构建好线程池ThreadPoolExecutor后就可以直接调用execute、submit方法执行线程任务:

  • execute适用于Runnable没有返回结果。
  • submit更适合Callable具有返回结果。

常见线程池类:

  1. newCachedThreadPool:创建一个可进行缓存重复利用的线程池。

  2. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,线程池中的线程处于一定的量,可以很好的控制线程的并发量。

  3. newSingleThreadExecutor:创建一个使用单个worker线程的Executor,以无界队列方式来运行该线程。线程池中最多执行一个线程,之后提交的线程将会排在队列中以此执行。

  4. newSingleThreadScheduledExecutor:创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期执行。

  5. newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行。

  6. newWorkStealingPool:创建一个带并行级别的线程池,并行级别决定了同一时刻最多有少个线程在执行,如不传并行级别参数,将默认为当前系统的CPU核心数*2。

线程池自定义参数

public ThreadPoolExecutor(
	int corePoolSize,
	int maximumPoolSize,
	long keepAliveTime,
	TimeUnit unit,
	BlockingQueue<Runnable> workQueue,
	ThreadFactory threadFactory,
	RejectedExecutionHandler handler
)
  1. corePoolSize线程池核心线程大小。线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

  2. maximumPoolSize线程池最大线程数量。一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

  3. keepAliveTime空闲线程存活时间。一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。

  4. unit空闲线程存活时间单位,keepAliveTime的计量单位。

  5. workQueue工作队列。新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

    • ArrayBlockingQueue基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
    • LinkedBlockingQueue基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
    • SynchronousQueue一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
    • PriorityBlockingQueue具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
  6. threadFactory线程工厂。创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  7. handler拒绝策略。当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略就是解决这个问题的,jdk中提供了4中拒绝策略:

    在这里插入图片描述

线程池的执行流程

在这里插入图片描述

线程池状态

在这里插入图片描述

ThreadPoolExecutor源码

类主要成员
public class ThreadPoolExecutor extends AbstractExecutorService {
	// ctl初始化了线程的状态和线程数量,初始状态为RUNNING并且线程数量为0
	// Integer既包含了状态也包含了数量,其中int类型一共32位,高3位标识状态,低29位标识数量
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
	// 这里指定了Integer.SIZE - 3,也就是32 - 3 = 29,表示线程数量最大取值长度
	private static final int COUNT_BITS = Integer.SIZE - 3;
    
	// 这里标识线程池容量,也就是将1向左位移上面的29长度
    // 并且 减去1 代表最大取值,二进制就是 000111..111
	private static final int CAPACITY = (1 << COUNT_BITS) - 1;
    
	// 这里是高三位的状态表示
	private static final int RUNNING = -1 << COUNT_BITS;   // 111
	private static final int SHUTDOWN = 0 << COUNT_BITS;   // 000
	private static final int STOP = 1 << COUNT_BITS; 	   // 001
	private static final int TIDYING = 2 << COUNT_BITS;    // 010
	private static final int TERMINATED = 3 << COUNT_BITS; // 011
    
	// 获取线程数量、状态等方式
	// 通过传入的c,获取最高三位的值,拿到线程状态吗
    // 最终就是拿1110 000……和c做&运算得到高3位结果
	private static int runStateOf(int c) { return c & ~CAPACITY; }
	// 获取当前线程数量,最终得到现在线程数量,就是拿c 和 0001 111……做&运算,得到低29位结果
	private static int workerCountOf(int c) { return c & CAPACITY; }
	// 这里是用来更新线程状态和数量的方式。
	private static int ctlOf(int rs, int wc) { return rs | wc; }
    
	private static boolean runStateLessThan(int c, int s) {
		return c < s;
	}
	
	private static boolean runStateAtLeast(int c, int s) { return c >= s; }
	// 判断当前线程池状态是否是Running。
	private static boolean isRunning(int c) { return c < SHUTDOWN; }
}
线程池的execute方法执行
public void execute(Runnable command) {
	// 首先是健壮性判断
	if (command == null)
		throw new NullPointerException();
    
	// 这里是获取核心线程数
	int c = ctl.get();
    
	// 判断工作线程是否少于核心线程数
	if (workerCountOf(c) < corePoolSize) {
		// 如果少于,需要创建新的任务线程
        // 如果addWorker返回false,证明核心线程初始化过了,返回true就直接结束
		if (addWorker(command, true))
			return;
		c = ctl.get();
	}
    
	// 如果线程池属于RUNNING状态,那就添加到任务队列,如果添加成功,进入
	if (isRunning(c) && workQueue.offer(command)) {
		// 重新获取ctl属性
		int recheck = ctl.get();
		// 再次判断是否是RUNNING状态,如果不是了,就执行remove删掉添加到阻塞队列的任务
		if (!isRunning(recheck) && remove(command))
			// 拒绝任务
			reject(command);
		// 如果线程数为0
		else if (workerCountOf(recheck) == 0)
			// 添加一个线程,避免出现队列任务没有线程执行的情况
			addWorker(null, false);
	}
    
	// 如果不能入队列,就尝试创建最大线程数
	else if (!addWorker(command, false))
		// 如果创建最大线程数失败,直接拒绝任务
		reject(command);
}
线程池的addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
    // 判断是否可以添加线程
	retry:
	for (;;) {
		int c = ctl.get();
		int rs = runStateOf(c);
        
		// 查看是否可以不去构建新的工作线程.
		if (rs >= SHUTDOWN // 如果线程状态不是RUNNING
		&&
		!(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
		// 线程不是SHUTDOWN状态(那也就是STOP,TIDYING,TERMINATED三者之一)
		// 任务不为空,且工作等待队列为null -> 这里对应上述的addWorker(null,false)
			return false;	// 这时直接return false,不去构建新的工作线程
		for (;;) {
			// 获取工作线程数
			int wc = workerCountOf(c);
			if (wc >= CAPACITY || // 如果工作线程大于最大线程容量
			wc >= (core ? corePoolSize : maximumPoolSize)) 
            // 或者当前线程数达到核心/最大线程数的要求
				return false; // 直接结束,添加Worker失败。
			if (compareAndIncrementWorkerCount(c)) // CAS的方式比较线程数量
				break retry; // 如果成功,跳出最外层循环
			c = ctl.get(); // 再次读取ctl的值
			if (runStateOf(c) != rs) 
            // 如果运行状态不等于最开始查询到的rs,那就从头循环一波。
				continue retry;
				// else CAS failed due to workerCount change; retry inner loop
		}
	}
    
	// 开始添加工作线程
	boolean workerStarted = false;
	boolean workerAdded = false;
	Worker w = null;
	try {
		w = new Worker(firstTask); // 构建Worker对象传入任务
		final Thread t = w.thread; // 将worker线程取出
		if (t != null) {
            // 拿到全局锁,避免我在添加任务时,其他线程干掉线程池,因为干掉线程池需要获取到这个锁
            final ReentrantLock mainLock = this.mainLock;
            // 加锁
            mainLock.lock();
        	try {
            	// 获取线程池状态
            	int rs = runStateOf(ctl.get());
            	// 两种情况允许添加工作线程
            	if (rs < SHUTDOWN || // 判断线程池是否是RUNNING状态
            		(rs == SHUTDOWN && firstTask == null)) { 
                    // 如果线程池状态为SHUTDOWN并且任务为空
                    
            		if (t.isAlive()) // 如果线程正在运行,直接抛出异常
            			throw new IllegalThreadStateException();
            		// 添加任务线程到workers中
            		workers.add(w);
            		// 获取任务线程个数
            		int s = workers.size();
            		// 如果任务线程大于记录的当前出现过的最大线程数,替换一下。
            		if (s > largestPoolSize)
            			largestPoolSize = s;
            		workerAdded = true; // 任务添加的标识设置为true
            	}
        	} finally {
        	    mainLock.unlock(); //任务添加成功,退出锁资源
        	}
			if (workerAdded) { // 如果添加成功
				t.start(); // 启动线程
				workerStarted = true; // 标识启动标识为true
			}
		}
	} finally {
		if (!workerStarted) // 如果线程启动失败
			addWorkerFailed(w); // 移除掉刚刚添加的任务
	}
	return workerStarted;
}
Worker内部包装类
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ 
// 实现了Runnable,意味着是一个线程
	// 线程对象
	final Thread thread;
	// 具体任务
	Runnable firstTask;
	// 有参构造
	Worker(Runnable firstTask) {
		setState(-1); // 添加标识,worker运行前,禁止中断(AQS)
		this.firstTask = firstTask;
		this.thread = getThreadFactory().newThread(this); 
        // 创建了一个新的线程,并且线程指向了当前对象,引入就是Worker
        // 所以在调用start方法时,调用的是Worker中的run方法
	}
        
	// run方法,线程的工作内容
	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 {
		// 任务不为null,就一致循环,否则调用getTask尝试从阻塞队列获取任务
		while (task != null || (task = getTask()) != null) {
		// 加锁的目的是表示当前任务正在执行,你shutdown任务也不会中断
			w.lock();
			if ((runStateAtLeast(ctl.get(), STOP) || // 判断线程池是否处于STOP状态
				(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
				&& !wt.isInterrupted() // 判断线程池是否中断
            )
				// 只要线程池STOP了,工作线程没有被中断,就中断线程
				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);	
	}
}
线程池的getTask方法
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

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

        // 如果状态大于0,代表线程池凉凉,
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount(); // 如果是STOP状态,或者阻塞队列为空
            return null;
        }
		// 获取工作线程数
        int wc = workerCountOf(c);

        // 查看是否允许
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 当前工作线程数大于最大线程数 ,后面判断表示是否是允许核心线程超时并且真的超时
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {   //工作线程 > 1 或者 阻塞队列为空
            if (compareAndDecrementWorkerCount(c))
            // 干掉当前工作线程并返回null,CAS的方式,如果失败,重新从头走一遍
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
                // 这里是可能出现超时情况并且允许回收线程,那就阻塞这么久拿阻塞队列的任务
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            	// 这里是线程阻塞在这,等待任务,不参与回收的情况,直到触发signal方法被唤醒,走catch继续下次循环
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
线程池的processWorkerExit方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 判断是否是认为停止,如果是要减掉一个工作线程数
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    // 加锁移除工作线程
    try {
        // 记录当前线程处理的任务数
        completedTaskCount += w.completedTasks;
        // 将worker从Set中移除
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
	// 尝试干掉线程池
    tryTerminate();

    int c = ctl.get();
    // 判断线程状态是否小于STOP。
    if (runStateLessThan(c, STOP)) {
        // 如果不是认为停止,需要判断线程是否需要追加一个线程处理任务
        if (!completedAbruptly) {
            // 查看核心线程是否允许超时
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                // 如果允许超时,并且工作队列不是空,就将min设置为1
                min = 1;
            if (workerCountOf(c) >= min)// 如果工作线程数量大于核心线程数,就直接结束
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值