一.概括
1.1创建线程池参数介绍
在创建线程池时都会调用线程池的构造方法
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:线程池的核心线程数,当线程池中的工作线程数小于核心线程数的时候,只要向线程池指派任务,线程池就会创建工作线程。
maximumPoolSize:线程池最大工作线程数,当线程池中的工作线程达到最大数的时候,即使再向线程池指派任务,线程池不会创建工作线程。
keepAliveTime:当线程池的工作线程数大于核心线程数的时候,多余的核心线程数的部分线程可以保持keepAliveTime的空闲时间,当keepAliveTime时间内还没有获取到任务,这些线程后就会被回收。
unit:保持空闲时间的时间单位。
workQueue:任务队列,当线程池里面核心线程都在工作的时候,再向线程池指派任务,线程池会将任务放入任务队列里,工作线程在执行完任务后会再向任务队列里取出任务来执行。
threadFactory:创建执行任务的工作线程。
handler:拒绝任务加入线程池的策越,当线程池里的线程已经达到最大数后,再向线程池里加派任务时,线程池会决绝执行这些任务,handler就是具体执行拒绝的对象。
线程池有四种拒绝策越:
1.AbortPolicy:直接抛出异常
2.DiscardOldestPolicy:从队列中丢掉最老的任务,然后再提交任务
3.DiscardPolicy:直接丢弃当前任务,什么也不做
4.CallerRunsPolicy:由调用线程处理该任务.
1.2四种方法创建线程池
通过Executors类可以创建四种不同的线程池
1.newFixedThreadPool方法创建一个工作线程数一定的线程池,可以向这个线程池无限指派任务
2.newCachedThreadPool方法创建一个工作线程不固定的线程池,这个线程池的工作线程数是灵活的,当向这个线程池指派任务的时候,线程池里如果有空闲的线程就用空闲的线程,如果没有就创建新的工作线程,理论上这个线程池的工作线程最多可以有Integer.MAX_VALUE个
3.newSingleThreadExecutor方法创建一个只有一个工作线程的线程池,当任务来了后,该线程池可以按照任务指派的先后顺序来执行。
4.newScheduledThreadPool方法创建一个按照一定时间周期来执行的线程池。
5.newWorkStealPool 一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。
newWorkStealingPool是jdk1.8才有的,会根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层用的ForkJoinPool来实现的。ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。
二、源码和例子
1.1、newFixedThreadPoola源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看到newFixedThreadPool方法创造出线程池的核心工作线程数和最大工作线程数的数量是一样的,这说明当线程池中的工作线程达到核心线程后,线程池中的工作线程不会再增长了。其中任务队列用的是LinkedBlockingQueue,LinkedBlockingQueue可以不用预设大小,当线程池里的工作线程都在忙碌的时候,还有多的任务就会被放入到这个阻塞队列中,理论上LinkedBlockingQueue可以无限的装入任务。
1.2newFixedThreadPool举例
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author 作者 wangbiao
* @date 创建时间:2017年2月24日 下午4:00:30
* @parameter
* @return
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) throws InterruptedException {
//ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
for(int i=0;i<15;i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
}
Thread.sleep(20000);
System.out.println("=============线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
executor.getQueue().size()+",已执行完的任务数目:"+executor.getCompletedTaskCount());
}
}
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("正在执行task "+taskNum+"===="+Thread.currentThread().getName());
try {
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+taskNum+"执行完毕====="+Thread.currentThread().getName());
}
}
输出结果
正在执行task 0====pool-5-thread-1
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 1====pool-5-thread-2
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 2====pool-5-thread-3
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 3====pool-5-thread-4
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 4====pool-5-thread-5
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:6,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:7,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:8,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:9,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:10,已执行玩别的任务数目:0
task 4执行完毕=====pool-5-thread-5
task 1执行完毕=====pool-5-thread-2
task 0执行完毕=====pool-5-thread-1
task 2执行完毕=====pool-5-thread-3
task 3执行完毕=====pool-5-thread-4
正在执行task 8====pool-5-thread-3
正在执行task 7====pool-5-thread-1
正在执行task 6====pool-5-thread-2
正在执行task 5====pool-5-thread-5
正在执行task 9====pool-5-thread-4
task 8执行完毕=====pool-5-thread-3
task 7执行完毕=====pool-5-thread-1
正在执行task 10====pool-5-thread-3
正在执行task 11====pool-5-thread-1
task 5执行完毕=====pool-5-thread-5
task 9执行完毕=====pool-5-thread-4
task 6执行完毕=====pool-5-thread-2
正在执行task 13====pool-5-thread-4
正在执行task 12====pool-5-thread-5
正在执行task 14====pool-5-thread-2
task 10执行完毕=====pool-5-thread-3
task 11执行完毕=====pool-5-thread-1
task 13执行完毕=====pool-5-thread-4
task 12执行完毕=====pool-5-thread-5
task 14执行完毕=====pool-5-thread-2
=============线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完的任务数目:15
当给newFixedThreadPool创建线程池设置参数为5的时候,从输出结果可以看出当向线程池指派任务的个数小于5的时候,线程池是每接受一个任务就创建一个线程,当任务大于5的时候,新来的任务就会被放到阻塞队列中。
2.1、newCachedThreadPool源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以看出newCachedThreadPool创建线程池的时候核心线程数是0,最大线程数是Integer.MAX_VALUE,这说明当来一个线程任务,线程池就会创建一个新的工作线程,除非此时有空闲的工作线程,工作线程空闲时间是60秒,,用的阻塞队列是SynchronousQueue,对这个队列有什么特征还不是很了解。
2.2、newCachedThreadPool举例
package threadPoolExecutor.test;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author 作者 wangbiao
* @date 创建时间:2017年2月24日 下午4:00:30
* @parameter
* @return
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) throws InterruptedException {
//ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 0, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(5));
//ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
for(int i=0;i<15;i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
}
Thread.sleep(20000);
System.out.println("=============线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
executor.getQueue().size()+",已执行完的任务数目:"+executor.getCompletedTaskCount());
}
}
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("正在执行task "+taskNum+"===="+Thread.currentThread().getName());
try {
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+taskNum+"执行完毕====="+Thread.currentThread().getName());
}
}
输出结果
正在执行task 0====pool-1-thread-1
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 1====pool-1-thread-2
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 2====pool-1-thread-3
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 3====pool-1-thread-4
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 4====pool-1-thread-5
线程池中线程数目:6,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 5====pool-1-thread-6
线程池中线程数目:7,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 6====pool-1-thread-7
线程池中线程数目:8,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 7====pool-1-thread-8
线程池中线程数目:9,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 8====pool-1-thread-9
线程池中线程数目:10,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 9====pool-1-thread-10
线程池中线程数目:11,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 10====pool-1-thread-11
线程池中线程数目:12,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 11====pool-1-thread-12
线程池中线程数目:13,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 12====pool-1-thread-13
线程池中线程数目:14,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 13====pool-1-thread-14
线程池中线程数目:15,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 14====pool-1-thread-15
task 1执行完毕=====pool-1-thread-2
task 0执行完毕=====pool-1-thread-1
task 2执行完毕=====pool-1-thread-3
task 3执行完毕=====pool-1-thread-4
task 6执行完毕=====pool-1-thread-7
task 9执行完毕=====pool-1-thread-10
task 5执行完毕=====pool-1-thread-6
task 8执行完毕=====pool-1-thread-9
task 4执行完毕=====pool-1-thread-5
task 7执行完毕=====pool-1-thread-8
task 14执行完毕=====pool-1-thread-15
task 13执行完毕=====pool-1-thread-14
task 10执行完毕=====pool-1-thread-11
task 11执行完毕=====pool-1-thread-12
task 12执行完毕=====pool-1-thread-13
=============线程池中线程数目:15,队列中等待执行的任务数目:0,已执行完的任务数目:15
可以看出newCachedThreadPool创建的线程池,当任务来了后就会创建新的线程去执行,但是这里的情况比较极端,如果任务被很快执行了,有空的工作线程还没有被回收,这样的工作线程会被复用。
3.1newSingleThreadPool源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
可以看出核心线程数和最大线程数都是一,空闲时间是0,阻塞队列用的是LinkedBlockingQueue,说明该线程池是用一个线程顺序的去执行任务。
由于举的例子不太好表现该线程池的特征,所以就不贴代码了。
4.1newScheduleThreadPool举例
package threadPoolExecutor.test;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author 作者 wangbiao
* @date 创建时间:2017年2月24日 下午4:00:30
* @parameter
* @return
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("执行前时间:"+new Date());
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
MyTask myTask = new MyTask(1);
executor.schedule(myTask, 10, TimeUnit.SECONDS);
//executor.scheduleWithFixedDelay(myTask, 3, 2, TimeUnit.SECONDS);
}
}
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println("执行后时间:"+new Date());
System.out.println("正在执行task "+taskNum+"===="+Thread.currentThread().getName());
try {
Thread.currentThread().sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+taskNum+"执行完毕====="+Thread.currentThread().getName());
}
}
输出结果:
执行前时间:Thu Jul 06 11:34:27 CST 2017
执行后时间:Thu Jul 06 11:34:37 CST 2017
正在执行task 1====pool-1-thread-1
task 1执行完毕=====pool-1-thread-1
可以看出任务在被延迟10秒后执行
executor.scheduleWithFixedDelay(myTask, 3, 2, TimeUnit.SECONDS);
这行代码会让任务延迟三秒执行,每两秒执行一次。
5.1 线程池的execute方法和submit方法的区别
其实最终submit方法还是调用execute方法来执行的,只是在调用submit方法的时候,会对传入的Runnable实现类进行封装,将任务封装成为FutureTask,然后调用execute方法执行,最后用FutureTask来承接执行的结果
下面是submit的源码
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
三、总结
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭