线程池

线程池的目的就是减少多线程创建的开销,减少资源的消耗,让系统更加的稳定。在web开发中,服务器会为了一个请求分配一个线程来处理,如果每次请求都创建一个线程,请求结束就销毁这个线程。那么在高并发的情况下,就会有大量线程创建和销毁,这就会降低系统的效率。线程池的诞生就是为了让线程得到重复使用,减少了线程创建和销毁的开销,减少了线程的创建和销毁自然的就提高了系统的响应速度,与此同时还提高了线程的管理性,使线程可以得到统一的分配,监控和调优。

线程创建和销毁为什么会有开销呢,因为我们java运行的线程是依赖于计算机内核的核心线程的。java创建的线程是用户层的线程,要依赖于线程调度去是用内核层的线程来执行,在执行销毁的时候会通过TSS在用户层和核心层的切换,这个切换就是很大的一笔开销。具体结构如下图:

线程实现方式

线程主要通过实现Runnable或者Callable接口来实现.Runnable与Callable的区别在于后者有返回值,但是前者没有返回值。

public interface Runnable {
public abstract void run();
} publuic interface Callable<V>{
V call() throws Exception;
}

下面我们来看一下测试代码:

package com.test.excutor;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit; public class ExcutorTest {
public static void main(String[] args) {
Thread t =new Thread( new RunTask());
t.start();
FutureTask<Object> ft=new FutureTask<Object>(new CallTask());
Thread f=new Thread(ft);
f.start();
try {
System.out.println("callTask output:"+(String)ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} class RunTask implements Runnable{ @Override
public void run() {
System.out.println(" RunTask Thread Name is "+Thread.currentThread().getName());
} } class CallTask implements Callable<Object>{ @Override
public Object call() throws Exception {
TimeUnit.SECONDS.sleep(1);
//System.out.println("This is callTask");
return "callTask answer";
} }
//运行结果:

RunTask Thread Name is Thread-0
callTask output:callTask answer

 

什么时候使用线程池:

1、单个任务处理时间比较短

2、需要处理的任务数量很大

Executor框架

Executor接口是Executor框架的一个最基本的接口,Executor框架的大部分类都直接或间接地实现了此接口。

它只有一个方法

void execute(Runnable command): 在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。

以下是框架图:

从以上图中可以看出ExecutorService就是继承了Executor接口的一个重要接口类。在这个接口类中定义了线程池的具体行为:

1、execute(Runnable command):履行Ruannable类型的任务,
2、submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
3、shutdown():在完成已提交的任务后封闭办事,不再接管新任务,
4、shutdownNow():停止所有正在履行的任务并封闭办事。
5、isTerminated():测试是否所有任务都履行完毕了。
6、isShutdown():测试是否该ExecutorService已被关闭。 
7、awaitTermination(long timeout, TimeUnit unit):阻塞,直到关闭后所有任务都已完成执行。请求,或发生超时,或当前线程中断,以先发生者为准。
8、submit(Callable<T> task):提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。如果想立即阻塞任务的等待,则可以使用 result = exec.submit(aCallable).get(); 形式的构造。
9、submit(Runnable task, T result):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果。
10、submit(Runnable task):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。

11、invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException:执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。注意,可以正常地或通过抛出异常来终止已完成 任务。如果正在进行此操作时修改了给定的 collection,则此方法的结果是不确定的。

12、invokeAll(Collection<? extends Callable<T>> tasks, long timeout,TimeUnit unit) throws InterruptedException:超时等待,同上。

13、invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException,ExecutionException:与 invokeAll的区别是,任务列表里只要有一个任务完成了,就立即返回。而且一旦正常或异常返回后,则取消尚未完成的任务。

14、invokeAll(Collection<? extends Callable<T>> tasks,long timeout,TimeUnit unit) throws InterruptedException: 超时等待,同上

线程池的创建

线程池的创建是通过Executors来创建的。比如说你需要创建一个固定大小的线程池我们可以使用Executors.newFixedThreadPool(n)来实现。当然还有很多其他的方法

我们来看一下:

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
//只允许一个线程执行
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
//可以无限的创建线程,会把创建的线程放在一个特殊的队列中去排队
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
//定时线程池
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

 
 
newFixedThreadPool(int nThreads)方法中的几个参数如下:
nThreads:核心线程数量
nThreads:最大线程池的数量
0L: 如果线程已经工作完毕,没有任务调用,该线程最大的存活时间
TimeUnit.MILLISECONDS:计时时间,单位为ms
new LinkedBlockingQueue<Runnable>():当核心线程全都在工作,没有空闲,此时会将多余的线程放到阻塞队列中排队,当核心线程执行完成以后再从阻塞队列当中拿出来继续执行。
下面我们来跑一段代码:
public class ExecutorTest {
public static void main(String[] args) {
Thread t =new Thread( new RunTask());
t.start();
FutureTask<Object> ft=new FutureTask<Object>(new CallTask());
Thread f=new Thread(ft);
f.start();
try {
System.out.println("callTask output:"+(String)ft.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Executor Test Start");
ExecutorService exe=Executors.newFixedThreadPool(5);
for(int i=0;i<20;i++){
exe.execute(new RunTask());
exe.submit(new RunTask());
}
/*//验证线程创建慢
if(exe.isShutdown()){
System.out.println("Thread 已经stop,等待线程创建");
exe.execute(new RunTask());
System.out.println("Executor Test end");
}else{
exe.shutdownNow();
} */ }
}
//结果如下:
RunTask Thread Name is Thread-0
callTask output:callTask answer
Executor Test Start
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-2
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-5
RunTask Thread Name is pool-1-thread-1
RunTask Thread Name is pool-1-thread-4
RunTask Thread Name is pool-1-thread-3
RunTask Thread Name is pool-1-thread-2

根据代码的测试结果我们可以看出线程1,线程2已经跑了好久了线程3,4,5才创建好,这个从侧面验证了线程的创建是很耗时的。

具体我们来看一下线程池类的工作流程和工作原理:

线程池线程的大小=核心线程+非核心线程

线程池的工作原理如上图所示已经非常清晰了,文字描述一下具体步骤:

1、启动线程池执行任务的时候先创建核心线程来执行任务;

2、核心线程数量创建达到规定值以后,还有任务没有线程执行的话就将任务放到阻塞队列中取排队等待;

3、等到队列排满了还有线程需要执行的话就创建非核心线程;

4、非核心线程还不够执行任务的话就直接执行拒绝策略。

线程池的重要属性

在ThreadPoolExecutor中有以下几个比较重要的属性:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl是一个控制状态,对线程池的运行状态和线程池中的有效线程数量进行控制的一个字段。它包含两部分的信息:

  1、线程池的运行状态(runState),高3位保存运行状态.相关方法是private static int runStateOf(int c){ return c& ~CAPACITY;}

  2、线程池内的有效线程数量(workerCount),ctl 是个Integer类型的数据,低29位保存;相关方法是 private static int workerCountOf(int c){ return c& ~CAPACITY;}

  3、控制状态的方法是:private static int ctlOf(int rs,int wc){return rs|wc;}

private static final int COUNT_BITS = Integer.SIZE - 3;( Integer.SIZE=31

count_bits=29,1<<29,也就是说workerCount最大值(2^29)-1(约5亿)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;

容量=2^29-1。

线程池的5种状态:

1、RUNNING:private static final int RUNNING = -1 << COUNT_BITS;高3位111

状态说明:线程池处于Running状态的时候,能够接收新的任务,并且对已添加的任务进行处理,任务不能大于规定的最大任务数;

状态切换:线程池的初始状态是RUNNING,也就是说线程池一旦被创建就处于RUNNING状态,并且线程池中的任务数量为0;

2、SHUTDOWN:private static final int SHUTDOWN = 0 << COUNT_BITS;高三位为000

状态说明:线程池处于SHUTDOWN状态的时候,不接收新的任务,但是可以处理已经添加的任务;

状态切换:调用线程池的shutDown()方法的时候,线程状态由RUNNING ---->>>>SHUTDOWN
3、 STOP :private static final int STOP = 1 << COUNT_BITS;高三位为001

状态说明:线程池处于该状态的时候,不接收新的任务,不处理已接收的任务,并且还会中断正在处理的任务,中断并不代表线程被杀死了,并且清空阻塞队列。

状态切换:线程池调用shutDownNow()接口的时候,线程池由RUNNING(SHUTDOWN)-------->>>STOP
4、TIDYING :private static final int TIDYING = 2 << COUNT_BITS;高三位为010

状态说明:当所有的任务已经终止,ctl的值为0,线程池会变成TIDYING状态,当线程池处于该状态的时候会执行钩子函数terminated().terminated()方法在ThreadPoolExecutor中是空的,用户想在线程池变为TIDYING状态的时候处理东西,可以通过重载terminated()方法实现。

状态切换:当线程池处于SHUTDOWN状态,并且阻塞队列中的任务为0,就会SHUTDOWN----->>>TIDYING,当线程池处于STOP状态下,线程池中执行任务数量为0,那么线程池状态STOP----->>>TIDYING.
5、TERMINATED:private static final int TERMINATED = 3 << COUNT_BITS;高三位为011

状态说明:线程池已经彻底终止,就会变成TERMINATED状态

状态切换:线程池处于TIDYING状态,执行完terminated()方法以后,就会实现TIDYING----->>>TERMINATED

进入该状态的条件如下:

1)线程池不是RUNNING状态;

2)线程池不是TIDYING状态或者TERMINATED状态;

3)如果线程池状态是SHUTDOWN,并且阻塞队列中的任务数量为0;

4)workerCount=0;

5)设置TIDYING状态成功;

线程池状态的切换如下图所示:

线程池的默认实现-ThreadPoolExecutor

创建方法:

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

任务的提交:

public void execute():提交任务没有返回值

public Future<?> submit():提交任务有返回值

参数解释:

corePoolSize:核心线程数,当提交一个任务的时候,创建一个核心线程,直到达到核心线程的最大数量。

maximumPoolSize:线程池最大容量,即该线程池最大允许的线程数量,当当前的阻塞队列满的时候,还有任务继续提交,则创建新的非核心线程继续执行,但是线程总数不能大于最大容量maximumPoolSize。

keepAliveTime:空余线程存活时间,即当线程池的核心线程数达到最大的时候,没有新的任务提交,那么非核心线程不会立即销毁,而是等待,等待时间大于keepAliveTime才会进行销毁。
unit:keepAliveTime的时间单位

BlockingQueue<Runnable> workQueue:阻塞队列,当核心线程数达到最大值的时候,还有新任务提交则将这些线程放到该队列中进行排队等待,提交的任务必须实现Runnable接口。除了这个阻塞队列以外,还有以下的阻塞队列:

1、ArrayBlockingQueue:基于数组结构的有界队列,遵循FIFO原则。
2、LinkedBlockingQueue:基于链表的有界队列,遵循FIFO原则,吞吐量高于ArrayBlockingQueue。

3、SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;

4、priorityBlockingQuene:具有优先级的无界阻塞队列;

threadFactory:用来创建线程,默认使用Executors.defaultThreadFactory() 来创建线程使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。

RejectedExecutionHandler handler:线程池的拒绝策略,线程池的拒绝策略有4种:

1、AbortPolicy:直接抛出异常,这是默认策略;

2、CallerRunsPolicy:用调用者所在的线程来执行任务:

3、DiscardOldestPolicy:抛弃阻塞队列中最靠前的任务,并执行当前任务;

4、DiscardPolicy:直接丢弃任务

线程池监控:

 public int getPoolSize():获取当前线程池的大小

public int getActiveCount():线程池中正在执行任务的线程数量

public int getLargestPoolSize():获取线程池中出现出现过的最大线程数量

public long getTaskCount():获取线程池已执行和未执行的线程总数

 public long getCompletedTaskCount():获取已经完成的任务的数量


 线程池的工作原理图如下:

详细的工作原理图请参考线程池的执行过程图。线程池中创建线程并执行用的方法是addWorker(Runnable firstTask, boolean core)方法。其中的firstTask用于指定新增线程执行的第一个任务,如果没有任务执行可以为null;boolean core 主要是判断当前线程池中活动的核心线程数是否达到最大,如果达到最大的话就创建非核心线程,该值为false,如果没有达到最大核心线程数量,则为true,创建核心线程。

private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
       //获取线程池的运行状态
int c = ctl.get();
int rs = runStateOf(c); // Check if queue empty only if necessary.当rs的值>=SHUTDOWN说明线程池不再接收新的任务了,然后判断以下三个条件:
      1、rs==SHUTDOWN 表示此时是关闭状态,不再接收新的任务,但是可以继续处理阻塞队列中的任务
      2、firstTask==null:firstTask为空
      3、!workQueue.isEmpty():阻塞队列不为空
      以上三个条件有一个不满足,则返回false,不创建新线程。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
int wc = workerCountOf(c);//获取工作线程数
if (wc >= CAPACITY || //判断线程数是否大于初始容量,即ctl的低29位都为1
wc >= (core ? corePoolSize : maximumPoolSize))//根据core的值,为true,则将wc与核心线程数来比较,如果为false,就跟线程池最大容量相比
return false; //如果以上两个条件满足则说明线程池已经满了,不能创建新线程,返回false
if (compareAndIncrementWorkerCount(c))//尝试增加workerCount,增加成功跳出此层for循环
break retry;
c = ctl.get(); // Re-read ctl 增加workerCount失败,重新获取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 {
w = new Worker(firstTask);//根据firstTask创建worker
final Thread t = w.thread;//每个worker创建一个线程
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());//获取线程池的运行状态 if (rs < SHUTDOWN || //状态是RUNNING状态
(rs == SHUTDOWN && firstTask == null)) {//或者状态处于SHUTDOWN,且firstTask为空(SHUTDOWN不接收新任务但是任然执行队列中的任务)
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);//向线程池中添加线程,workers是一个HashSet
int s = workers.size();//获取线程池数量
if (s > largestPoolSize)//largestPoolSize记录着线程池中出现过的最大线程数量,如果此时的线程数量大于之前的largestPoolSize,则重新赋值
largestPoolSize = s;
workerAdded = true;//线程添加成功
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//运行线程
workerStarted = true;//线程运行成功
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}

Worker类

worker类封装了线程池中的每一个线程,线程池ThreadPool本质上维护的就是worker类,该类继承了AQS,实现了Runnable接口。worker类包含属性thread,firstTask,completedTasks三个属性。

firstTask:保存传入的任务

thread:执行任务创建的线程,在构造方法中通过getThreadFactory().newThread(this)创建线程,传入的是this,其实worker本身就实现了Runnable接口,所以worker本身就是一个线程,在线程启动的时候就会调用worker类的run方法。Worker类继承AQS,而不是ReetrantLo是因为AQS使用的是独占锁,是不可以重入的。lock一旦获取到了独占锁,就表明线程在运行中,就不应该中断。如果线程不是在独占状态,那么就说明该线程是空闲线程,可以进行中断。线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线interruptIdleWorker方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;之所以设置为不可重入,是因为我们不希望任务在调用像setCorePoolSize这样的线程池控制方法时重新获取锁。如果使用ReentrantLock,它是可重入的,这样如果在任务中调用了如setCorePoolSize这类线程池控制的方法,会中断正在运行的线程。

         final Thread thread;
Runnable firstTask;
volatile long completedTasks; /**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker是因为AQS中默认的state是0,如果刚创建了一个Worker对象,还没有执行任务时,这时就不应该被中断
        
      this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}

在上述内容中我提到了,线程池中创建的线程主要是worker,线程的执行也是worker,worker在执行的时候调用的run方法,代码里的run方法调用的就是runWorker()方法,下面我们就来解读一下runWorker()方法:

 final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts 将state设置为0
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {//任务不为空进入循环 getTask()从阻塞队列中获取任务
w.lock();//线程运行上锁
/*如果线程正在停止,保证线程处于中断状态,如果不是的话保证当前线程不是中断状态;
           这里需要考虑执行If语句期间也执行了SHUTDOWNNOW方法,将状态直接置为STOP,同时还会中断线程池中的所有线程wt.interrupted()来判断是否中断,是为了确保在RUNNING和SHUTDOWN状态的时候是处于非中断状态。
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++;//完成任务数量+1
w.unlock();//释放锁
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}

根据上述的代码以及注释,现在来总结一下,线程运行的runWorker()方法总体的运行流程如下:

1、while循环,加锁,从阻塞队列中获取任务(getTask())

2、如果线程正在停止,保证当前线程处于中断状态,如果不是则保证当前线程不是中断状态

3、运行任务内容

4、任务运行完成,释放锁。

5、当获取的任务为空跳出while循环,执行 processWorkerExit(w, completedAbruptly)方法。

除了runWorker之外,上述还有提到getTask(),从阻塞队列中获取任务:

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.线程池是SHUTDOWN以上的状态的时候,判断线程池是否停止,或者阻塞队列是否为空,如果都是的话那么workerCout减1
        并且返回空值。因为在线程池在SHUTDOWN以上的状态的时候应该,不接收新的任务,也不允许往阻塞队列当中添加新的任务。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
       //获取线程的数量
int wc = workerCountOf(c); // Are workers subject to culling?timed判断是否需要进行超时控制。allowCoreThreadTimeOut参数是允许核心线程超时标识,默认是false.同时也判断当前线程
数量是否是大于核心线程的数量的。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
       //当当前工作线程数量大于规定线程池最大容量,或者超时控制为真的时候,同时判断当前线程数大于1或者阻塞队列是空的两个条件,当两个条件中有一个为真,那么将workerCount-1,
c成功的话就返回null值,失败的话就重试。该判断比较重要,主要是当线程池中的线程数量处于大于corePoolSize,但是又小于maximumPoolSize的状态下,获取任务超时,说明阻塞队列为空,也就说明
现在线程池不需要那么多线程来执行任务,需要把corePoolSize的线程销毁,让线程数量维持在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();//timed =true 那么就通过阻塞队列的poll方法进行超时控制,否则的话就从队列中获取任务。
if (r != null)
return r;//获取任务成功,返回获取的任务。
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}

在runWorker()方法中,getTask失败,是会跳出while 循环,执行processWorkerExit()方法:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // 如果执行线程出现异常,那么workerCount-1
decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;//completedTaskCount统计完成任务的线程数,同时移除已经执行完任务的worker
workers.remove(w);
} finally {
mainLock.unlock();
} tryTerminate(); int c = ctl.get();
if (runStateLessThan(c, STOP)) {//线程的状态是RUNNING或者SHUTDOWN进行以下判断
if (!completedAbruptly) {//如果线程非异常结束进行以下操作
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;//如果允许核心线程超时,那么min=0,如果不允许那么min=核心线程的数量
if (min == 0 && ! workQueue.isEmpty())
min = 1;//如果核心线程允许超时,min==0且阻塞队列不为空的,min=1,即至少保证线程池中有1个worker
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);//如果worker异常结束那么就addWorker
}
}

综上所述,线程池中线程的执行过程大致如下:

也就是说,当线程池在执行Executors.newFixedThreadPool(n).execute(Runnable)的方法的时候,就进入到线程池ThreadPoolExecutor中去执行execute(Runnable)方法,该方法主要addWorker(),在执行addWorker的时候,worker类会创建线程getThreadFactory().newThread(this),创建好线程以后,线程会启动,t.start()实际调用的就是worker类中的run()方法,该方法的实质是运行runWorker()方法,在执行该方法的时候就会从阻塞队列中获取任务,获取任务成功以后执行线程,完成任务即可。

Executor线程池原理详解的更多相关文章

  1. JAVA线程池原理详解二

    Executor框架的两级调度模型 在HotSpot VM的模型中,JAVA线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也 ...

  2. JAVA线程池原理详解一

    线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线 ...

  3. JAVA线程池原理详解(1)

    线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线 ...

  4. Java—线程池ThreadPoolExecutor详解

    引导 要求:线程资源必须通过线程池提供,不允许在应用自行显式创建线程: 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题.如果不使用线程池,有可能造成系统 ...

  5. 用 ThreadPoolExecutor/ThreadPoolTaskExecutor 线程池技术提高系统吞吐量(附带线程池参数详解和使用注意事项)

    1.概述 在Java中,我们一般通过集成Thread类和实现Runnnable接口,调用线程的start()方法实现线程的启动.但如果并发的数量很多,而且每个线程都是执行很短的时间便结束了,那样频繁的 ...

  6. JDK提供的四种线程池代码详解

    一.线程池什么时候使用,会给我们带来什么好处? 如果很多用户去访问服务器,用户访问服务器的时间是非常短暂的,那么有可能在创建线程和销毁线程上花费的时间会远远大于访问所消耗的时间,如果采用线程池会使线程 ...

  7. 线程池ThreadPool详解

    http://www.cnblogs.com/kissdodog/archive/2013/03/28/2986026.html 一.CLR线程池 管理线程开销最好的方式: 尽量少的创建线程并且能将线 ...

  8. unix中的线程池技术详解

    •线程池就是有一堆已经创建好了的线程,当有新的任务需要处理的时候,就从这个池子里面取一个空闲等待的线程来处理该任务,当处理完成了就再次把该线程放回池中,以供后面的任务使用,当池子里的线程全都处理忙碌状 ...

  9. 三、VIP课程:并发编程专题->01-并发编程之Executor线程池详解

    01-并发编程之Executor线程池详解 线程:什么是线程&多线程 线程:线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系 ...

随机推荐

  1. cmd命令查看已连接的WiFi密码

      实验环境:Windows 10.命令提示符(管理员权限) 一.CMD命令查看WiFi密码 使用方法: ①.运行CMD(命令提示符) (确保无线网卡启用状态)②.输入命令查看WiFi配置文件:  # ...

  2. 快速了解TCP的流量控制与拥塞控制

    有关TCP你不能不知道的三次握手和四次挥手问题,点我跳转 流量控制 1. 滑动窗口 数据的传送过程中很可能出现接收方来不及接收的情况,这时就需要对发送方进行控制以免数据丢失.利用滑动窗口机制可以很方便 ...

  3. 安装Harbor管理镜像服务

    Harbor是什么? 还记得Docker Registry么?它是Docker官方提供的镜像仓库,简单易用,一键就可以部署.使用. 虽然看起来不错,但是Registry有些问题需要解决: 没有图形界面 ...

  4. Python集训营45天—Day03

    目录 1. 分支结构 1.1 初步介绍 1.2 使用案例 1.3 练习 2.循环结构 1.1 初步介绍 1.2 使用案例 1. 分支结构 1.1 初步介绍 至今,我们所写的Python代码都是顺序执行 ...

  5. Python中使用pip安装库时提示:远程主机强迫关闭了一个现有的连接

    场景 在cmd中使用pip install moviepy时,需要安装一些依赖库,很长时间后提示: 远程主机中断了一个现有的连接. 原因是默认镜像源下载过慢,将其修改为国内或者设置安装时的源. 这里以 ...

  6. yolo进化史之yolov3

    yolov3的论文写的比较简略,不看yolov1,yolov2很难直接看懂. 建议先看v1,v2论文. yolov3主要做了几点改进 改进了特征提取部分的网络结构 多尺度预测 分类由softmax改为 ...

  7. Node.js之模块机制

    > 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...

  8. Flink cep的初步使用

    一.CEP是什么 在应用系统中,总会发生这样或那样的事件,有些事件是用户触发的,有些事件是系统触发的,有些可能是第三方触发的,但它们都可以被看做系统中可观察的状态改变,例如用户登陆应用失败.用户下了一 ...

  9. oracle 11g 下载安装 使用记录

    Oracle 11g 使用记录 1.下载oracle快捷安装版:   (1)下载连接:https://pan.baidu.com/s/1ClC0hQepmTw2lSJ2ODtL7g 无提取码 (2)去 ...

  10. windows导出文件名列表

    新建txt文件,粘贴以下命令: DIR *.*  /B >LIST.TXT 将txt文件的后缀改为.bat 执行bat文件即可