之前学习的并发知识,现在记录一下

主要参数说明

线程池(ThreadPoolExecutor),Jdk1.5版本开始使用,构造方法参数如下(来自ThreadPoolExecutor源码):

corePoolSize:核心线程数,线程池启动时就会创建的线程数量。即使核心线程是空闲的,也不会被回收,除非

调用了allowsCoreThreadTimeOut方法为true

executorService.allowCoreThreadTimeOut(true);

maximumPoolSize:最大线程数,线程池中最大的线程数量

keepAliveTime:线程超时时间,看源码可知,该参数的意义是线程从工作队列中取出任务的超时时间。其中,timed是指是否设置allowCoreThreadTimeOut为true,如果没有设置,则判断当前线程数是否大于核心线程数。timeout指的就是线程从队列中取任务是否超时。当一个线程从工作队列中取任务时,满足以下条件:(当前线程数大于最大线程数 或 (timed为true且该线程获取任务超时))&(当前线程数大于1 或 工作队列为空),这个线程就会被回收。

// 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;
            }

unit:超时时间的单位

workQueue:工作队列,详情见下面的说明

threadFactory:线程工厂,要实现ThreadFactory接口,线程池创建线程时会调用ThreadFactory的newThread方法创建线程。

RejectedExecutionHandler:饱和策略,详情见下面的说明

工作队列

工作队列和线程池创建线程的行为息息相关,下面就两种情况来讨论一下:

1.线程池设置了核心线程数(核心线程数不为0)

    在调用excute方法提交任务后,会先判断当前线程池中的线程个数,如果当前线程数小于核心线程数,那么线程池就会创建一个新的线程,执行任务。否则,线程池会尝试将任务放入工作队列中,如果成功放入,且当前可用线程数为0,线程池就会创建一个线程,执行任务,否则使用线程池中存在的线程执行任务。如果放入工作队列失败(队列已满),并且当前线程数大于等于最大线程数,就不会再创建线程,使用饱和策略拒绝任务,否则就会创建一个线程,接手新提交的任务。

//获取当前线程数量
	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);
	    //如果当前线程数为0,创建线程,但是新线程不接手当前任务,而是从队列中获取任务。
	    else if (workerCountOf(recheck) == 0)
	        addWorker(null, false);
	}
	//放入队列失败,直接创建新的线程,接手任务。如果创建线程失败,拒绝任务
	else if (!addWorker(command, false))
	    reject(command);

 2.线程池没有设置核心线程数(核心线程数为0) 

    如果线程池没有设置初始线程,在调用excute方法后,会直接将任务放入工作队列,接下来的步骤和1中的一致,这里不再赘述

    当一个线程执行完任务后,会再次从工作队列中取出任务执行。如果此时工作队列已经没有任务了,那么该线程就有可能按照超时时间的逻辑被回收,直到当前线程数和核心线程数一样时,不再回收线程,此时线程池还是处于运行状态,等待任务到来。但是,如果设置了allowCoreThreadTimeOut为true,那么所有线程都会被回收,线程池自动终止运行。

饱和策略

    如果线程池中的线程数量大于等于最大线程数,且工作队列已满,新提交的任务就会被拒绝,此时就要使用饱和策略。默认饱和策略是AbortPolicy,该策略会抛出rejectedExecution,调用者可以捕获该异常,编写处理代码。除了默认的饱和策略,还有其他几种饱和策略:

    >DiscardPolicy:抛弃策略,会悄悄的抛弃没法加入到队列的任务

    >DiscardOldestPolicy:抛弃最旧策略,会悄悄的抛弃下一个要执行的任务。如果使用的是优先队列,会抛弃优先级最高的任务,最好不要使用。

    >CallerRunsPolicy:调用者运行策略,即不会抛出异常,也不会放弃执行。任务会被调用了execute方法的线程调用。比如,在main方法中开启线程池,如果线程池使用该策略,那么饱和的任务会被主线程调用。 

    可以使用下面的代码设置饱和策略

testThreadPoolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());