java线程池详细入门教程即源码解析

xiaoxiao2021-02-28  34

##1、线程池概念      线程池是线程的集合,通过线程池我们不需要自己创建线程,将任务提交给线程池即可。为什么要使用线程池,首先,使用线程池可以重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗。其次,由于没有线程创建和销毁时的消耗,可以提高系统响应速度。最后,通过线程可以对线程进行合理的管理,根据系统的承受能力调整可运行线程数量的大小等。 ##2、线程池使用示例      这里做两个测试,首先写一个实现Runnable接口的类:

class MyTask implements Runnable{ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()); } }

main中代码:

ExecutorService pool = Executors.newFixedThreadPool(5); Runnable myTask = new MyTask(); Runnable myTask1 = new MyTask(); Runnable myTask2 = new MyTask(); Runnable myTask3 = new MyTask(); Runnable myTask4 = new MyTask(); pool.execute(myTask); pool.execute(myTask1); pool.execute(myTask2); pool.execute(myTask3); pool.execute(myTask4);

这里newFixedThreadPool中的5代表可运行线程数量。所以结果:

pool-1-thread-3 pool-1-thread-1 pool-1-thread-4 pool-1-thread-5 pool-1-thread-2

可以看到这里又五个线程在运行。这里如果将可运行线程数量变成2,那么结果如下:

pool-1-thread-2 pool-1-thread-1 pool-1-thread-2 pool-1-thread-1 pool-1-thread-2

可以看到线程1和线程2都被重复利用了,没有创建更多的线程出来。 ##3、线程池的状态      线程池的状态及状态间的切换如下图所示: 可以看到线程池有五种状态RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED,不同于线程的转台切换的是,线程池的状态可操作的其实只有两种状态,RUNNING和TERMINATED,线程池一旦创建就会处于RUNNING状态,而TERMINATED是终止,线程池调用shutdown或者shutdownNow线程池其实就会慢慢的切换到终止状态,中间三种状态只是线程由运行态过渡到终止态的中间状态,用来进行一些处理事项的。RUNNING状态是正常运行状态,能够接受新的任务和处理任务,SHUTDOWN是关闭状态,不能接受新的任务,将正在执行的任务处理完毕后进入到TIDYING整理状态,STOP是停止状态,不接受任务,也不处理任务,直接中断任务,然后进入到TIDYING整理状态,TIDYING状态会自动调用terminate方法,调用完后进入到终止状态。 ##4、通过源码理解线程池      首先是线程的创建,线程的创建一般通过一个工厂类创建,

ExecutorService pool = Executors.newFixedThreadPool(2);

进去看这个函数:

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }

可以看到还是直接new了一个线程池出来,再看这个线程池的构造方法:

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.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }

首先对这几个参数进行说明: corePoolSize:核心池大小,表示线程池正常情况下能运行的线程最大多少。 maximumPoolSize:最大池大小,表示线程池最大能运行的线程数。 keepAliveTime:表示线程执行任务后的空闲存活时间,过了这个时间线程将被销毁,这个参数的存在也直接实现了概述中所说的前两点优点,线程的消耗可以分为A,B,C三个阶段,A代表创建,B代表执行,C代表消耗,如果有很多个任务需要执行时,B执行所带来的消耗代价小于A,C所带来的消耗代价,那么就通过这个参数,让线程保持一段存活时间,方便执行后面的任务。 unit:线程空闲存活时间的单位。 workQueue:这个是任务队列,将后面的任务加入到这个队列中。 threadFactory:真正用于创建线程的参数。 handler:表示已经不能在接收任务时,调用的处理类。 在这个构造方法中分别对这几个参数进行了初始化。**这里再介绍下这几个参数的关系,当用户创建任务时,如果当前的运行的线程数小于核心池数量,那么会创建新的线程来执行这个任务,如果当前运行的线程数大于核心池的数量,那么就将任务添加到任务队列中,如果任务队列已经装满且当前运行线程数小于最大池的数量,那么就再创建线程来执行这个任务,如果已经等于了最大池的数量,那么将拒绝这个任务并且执行handle处理类。**线程的创建介绍了再来看任务的执行方法execute:

public void execute(Runnable command) { //当任务为空,抛出空指针异常 if (command == null) throw new NullPointerException(); //这个是获取到ctl参数的值,这个值是32位的 //高三位存储了线程池的状态,其余位存储了线程池中运行的线程的数量 int c = ctl.get(); //当线程运行数量小于核心池时 if (workerCountOf(c) < corePoolSize) { //创建新的线程来执行这个任务 if (addWorker(command, true)) //然后直接返回 return; c = ctl.get(); } //如果当前运行线程已经超过了核心池大小,并且线程池是运行的 //将任务加入到任务队列中 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方法,这里继续看addWorker方法:

private boolean addWorker(Runnable firstTask, boolean core) { //前面这一片都是验证性的工作,可以不重点看 retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. 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; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } //从这里开始重点看 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { //获取可重入互斥锁 final ReentrantLock mainLock = this.mainLock; //创建一个新的线程来之心这个任务 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { //线程不为空,加锁 mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int c = ctl.get(); int rs = runStateOf(c); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); //将worker添加到set集合中 workers.add(w); //获取集合的worker数量 int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } //worker添加成功执行任务 if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }

到这里线程池的入门介绍结束。

转载请注明原文地址: https://www.6miu.com/read-1950116.html

最新回复(0)