java线程池(一)
参考:https://www.cnblogs.com/dolphin0520/p/3932921.html
https://blog.****.net/l_kanglin/article/details/57411851
线程池ThreadPoolExecutor.
继承关系:
ThreadPoolExecutor继承自抽象类AbstractExecutorService.
AbstractExcutorService实现了ExecutorService接口
ExecutorService接口继承了Executor接口
构造函数:
public ThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
thrownew IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
thrownew NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
共有四个构造函数,但是其它三个构造函数都是调用这个构造函数来完成对象的实例化的。
各参数的意义:
corePoolSize:表示线程池的大小,一旦创建,它就是这么大。
maximumPoolSize:表示线程池的最大的大小。当任务过多时,可能会创建新的线程来执行任务,但是总的线程池的大小不能超过这个maximumPoolSize,否则就会抛出异常,或者拒绝任务,这个根据拒绝策略来定。
KeepAliveTime:当线程池中线程的数量大于corePoolSize的时候,如果某个线程空闲时间超过keepAliveTime,就会销毁这个线程。
Unit:keppAliveTime的时间单位。
workQueue:任务缓存队列,当线程数量大于等于corePoolSize的时候,新来的任务就会被缓存到workQueue中。
ThreadFactory:用来创建新的线程的线程工厂。
RejectedExecutionHandler :拒绝处理任务的策略。
线程池的五种状态
// runState is stored in the high-order bits
privatestaticfinalintRUNNING =-1 << COUNT_BITS;
privatestaticfinalintSHUTDOWN = 0 << COUNT_BITS;
privatestaticfinalintSTOP = 1 << COUNT_BITS;
privatestaticfinalintTIDYING = 2 << COUNT_BITS;
privatestaticfinalintTERMINATED = 3 <<COUNT_BITS;
当线程池刚刚创建的时候处于RUNNING状态,当调用shutdown()方法的时候,线程池会处于SHUTDOWN状态,此时线程池不再接收新的任务,但是会继续把正在执行的任务和缓存队列中的任务都执行完了以后再销毁。当调用线程池的shutdownNow()方法的时候,线程池会进行STOP状态,此时线程池不再接收新的任务,并且会尝试去终止正在执行的任务和清空缓存对列。当线程池为空的时候,即线程池中没有执行的线程了的时候。就会进入TIDYING状态。此时线程池就会执行另一个钩子函数terminate(),执行完这个函数,线程池就会从TIDYING状态变为TERMINATED状态。
如下图:
线程池原理:
线程池创建之初,会设定corePoolSize和maximumPoolSize。并且不会创建线程,除非通过预创建的方法prestartAllCoreThreads()或者prestartCoreThread()。在没有预创建的情况下,就是来一个任务创建一个线程。当线程数到达corePoolSize的时候,就会把新来的任务放到缓存队列中。当缓存队列满了以后,就会创建新的线程。当线程数到达maximumPoolSize。再有新的任务过来,就会抛出异常,拒绝任务,并关闭线程池。但是已经在缓存队列中的线程和正在执行的线程会执行完。
当线程的数量大于corePoolSize的时候,如果某个线程的空闲时间超过KeepAliveTime,就会被销毁。
使用示例
importjava.util.concurrent.ArrayBlockingQueue;
importjava.util.concurrent.ThreadPoolExecutor;
importjava.util.concurrent.TimeUnit;
publicclass TreadPoolTest {
publicstaticvoid main(String[] args) {
ThreadPoolExecutor executor=new ThreadPoolExecutor(5,10, 200, TimeUnit.MILLISECONDS, newArrayBlockingQueue<Runnable>(5));
for(inti=0;i<15;i++) {
MyTaskmyTask=new MyTask(i);
executor.execute(myTask);
System.out.println("线程池数目"+executor.getPoolSize()+",队列中正在等待执行的任务数目:"+executor.getQueue().size()+
",已执行完别的任务数目:"+executor.getCompletedTaskCount());
}
executor.shutdown();
}
}
class MyTask implements Runnable{
privateinttaskNum;
public MyTask(inti) {
this.taskNum=i;
}
@Override
publicvoid run() {
System.out.println("正在执行task"+taskNum);
try {
Thread.currentThread().sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("task"+taskNum+"执行完毕");
}
}
输出结果:
正在执行task0
线程池数目1,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
线程池数目2,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task1
线程池数目3,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task2
线程池数目4,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task3
线程池数目5,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:1,已执行完别的任务数目:0
正在执行task4
线程池数目5,队列中正在等待执行的任务数目:2,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:3,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:4,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
线程池数目6,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task10
线程池数目7,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task11
线程池数目8,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task12
线程池数目9,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task13
线程池数目10,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task14
task0执行完毕
task1执行完毕
task2执行完毕
正在执行task6
正在执行task5
正在执行task7
task4执行完毕
task3执行完毕
正在执行task9
task10执行完毕
正在执行task8
task14执行完毕
task12执行完毕
task13执行完毕
task11执行完毕
从输出结果中可以看出,当缓冲队列满的时候,才会创建新的线程。如果把任务数改为20的时候,输出结果如下:
正在执行task0
线程池数目1,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
线程池数目2,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
线程池数目3,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task1
正在执行task2
线程池数目4,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
正在执行task3
线程池数目5,队列中正在等待执行的任务数目:0,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:1,已执行完别的任务数目:0
正在执行task4
线程池数目5,队列中正在等待执行的任务数目:2,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:3,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:4,已执行完别的任务数目:0
线程池数目5,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
线程池数目6,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task10
线程池数目7,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task11
线程池数目8,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task12
线程池数目9,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task13
线程池数目10,队列中正在等待执行的任务数目:5,已执行完别的任务数目:0
正在执行task14
Exceptionin thread "main" java.util.concurrent.RejectedExecutionException: Task [email protected] rejected [email protected][Running, pool size = 10,active threads = 10, queued tasks = 5, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
atjava.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at TreadPoolTest.main(TreadPoolTest.java:11)
task4执行完毕
task0执行完毕
task1执行完毕
正在执行task7
task2执行完毕
task11执行完毕
task3执行完毕
正在执行task9
task10执行完毕
正在执行task8
task12执行完毕
正在执行task6
正在执行task5
task13执行完毕
task14执行完毕
从上面可以看出,当缓冲队列满的时候,而且线程池中的线程已经为maximumPoolSize的时候,就会报出异常,但是报出异常之后并不会终止线程池中的线程,而是会把正在执行的线程和缓存队列中的线程都执行完了以后,再终止线程池。