线程池中的submit和execute有什么区别呢?
其实最终调用的都是execute方法,submit方法相当于把任务包装了一下,submit方法会把Runable或者Callable包装成FutureTask,submit提交后会返回一个Future,这个其实是一个FutureTask对象。
FutureTask作用?
Future表示获取未来的一个结果,task表示一个执行的任务,用户拿到Future对象后可以通过get方法获取结果,get方法会堵塞获取这个结果的线程,直到FutureTask被真正执行完成后,一直堵塞到拿到结果为止。
FutureTask的get方法,支持多个线程调用,任务完成前,多个线程在堵塞,并且任务完成后,需要唤醒多个堵塞的线程,唤醒的过程?
FutureTask任务的状态 NEW,表示FutureTask任务没有被任何线程处理过 COMPLETING,表示FutureTask已经被执行了,马上有结果了,一个临界状态 NORMAL,表示FutureTask正常执行完了,并且没发生任何异常,已经有结果了
当FutureTask执行get方法,先去判断当前FutureTask状态是不是已经执行完了,已完成,直接返回结果,如果是NEW或者COMPLETING状态,表示任务尚未结束,这时候调用get方法的线程,会把自己包装成一个WaitNode对象,使用头插的方法插入到一个等待队列,然后调用LockSupport.park()方法挂起,堵塞这个线程。 FutureTask被线程池中的某个线程执行get方法后,会去调用FutureTask的run方法,run方法调用的其实是submit提交的Runable或者Callable,当执行完业务方面代码,没有异常的情况下,run方法会更新FutureTask状态为NORMAL表示这个任务正常执行完成了,然后调用一个唤醒等待队列的方法,方法内遍历整个等待队列,然后调用LockSupport.unpark()唤醒WaitNode上的Thead,被唤醒的Thead继续调用FutureTask的get方法,这时候就能拿到任务的结果了。
Runable是没有返回值的,FutureTask的get方法是有返回值的,这块怎么处理的?
其实在FutureTask内部,就是把业务提交的Runable通过适配器转换成Callable,Runable转换成Callable这个接口没有返回值,业务层面调用FutureTask的get方法,返回值为null。
线程池的核心参数
核心线程数 最大线程数 空闲线程存活的时间 堵塞队列 theaddactory工厂实现类 拒绝策略
核心线程数和最大线程数两个参数的意义
提交任务的时候,如果线程池内的worker数量还没有达到核心线程数,就会创建一个worker,这个worker就是包装了一个Thead线程,这个worker有一个字段叫firstTask,worker线程启动后,优先执行firstTask内的任务,也就是说提交的task会被设置的worker.firstTask中。 最大线程数作用发生在任务队列满了后
当任务提交到线程池后,还没达到核心线程数,会创建一个新的worker,达到核心线程数后会提交到任务队列中,如果线程池中的线程消费速度赶不上生产的速度,队列会满,这时候会去检查一下当前线程数量是否达到最大线程数,没达到,创建一个新的worker,否则走拒绝策略。
线程池的五个状态
(1)RUNNING;
● 线程池处于RUNNING状态时,线程池能够接收新任务,也能够对已经添加的任务进行处理;
● 线程池一被创建,线程池的状态就是RUNNING状态;
(2)SHUTDOWN;
● 线程池已经被关闭了,不再接收新任务;但是,其还是会处理队列中的剩余的任务;
● 调用线程池的shutdown()方法后,线程池的状态就会由RUNNING转为SHUTDOWN;
(3)STOP;
● 线程池处于STOP状态,此时线程池不再接收新任务,不处理已经添加进来的任务,并且会中断正在处理的任务;
● 调用线程池的shutdownNow()方法后,线程池的状态就会由RUNNING或SHUTDOWN转为STOP;
(4)TIDYING;
● 线程池被下达关闭命令后,如果当前所有的任务都已经终止了(这个终止可以表示执行结束,也可以表示强制中断,也可以表示被丢弃) ,那么线程就会进入TIDYING状态;当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
● 如果线程状态已经是SHUTDOWN了,并且线程中以及队列中都没有任务时,线程池就会由SHUTDOWN转为TIDYING;如果线程池状态为STOP,那么当线程池把所有的任务都给清理干净时,线程池就会由STOP转为TIDYING;
(5)TERMINATED;
● 线程池就结束了;线程池就不能重新启动了;
● 如果线程池处于TIDYING状态,那么当线程池执行完terminated()方法后,线程池状态就会由TIDYING转为TERMINTED;
worker继承了AQS,并且使用AQS的独占锁,为什么要使用AQS的独占锁?
这个独占锁表示worker的状态,当这个worker的独占锁状态是占用状态,表示占用独占锁的线程就是worker内部封装线程的本身,表示正在工作中,worker内部线程在执行task.run方法,也就是说,worker在执行任务前,会先抢占这把锁,然后执行业务代码,执行完毕后释放锁,表示worker回归到空闲状态了