JUC线程池源码阅读1:ThreadPool的shutdown

LZ项目中用到JUC的线程池,发现调用shutdown和shutdownNow方法,池中的线程还是会继续执行。只是调用shutdownNow方法时,池中线程的阻塞方法(LZ代码中是sleep)会抛出InterruptedException。因此LZ要分析下这里面的具体实现。

1.shutdown
已经submit的task会继续执行(即使该task还未开始执行),不再接收新的task的submit。

  • checkShutdownAccess():java安全管理器检查应用程序,是否有操作线程的权限。关于java安全管理器
  • advanceRunState(SHUTDOWN):修改线程池状态,这里设置为SHUTDOWN(线程池状态机)。
  • interruptIdleWorkers():中断空闲的workers。

因为线程池中的每个线程,都保存在Worker中(ThreadPoolExecutor内部类Worker),1对1的关系。所以遍历Worker的集合,判断其中哪个线程可以中断。
PS:这里明确两个概念:

  1. workQueue:BlockingQueue。这个阻塞队列存储的是调用submit提交的那个Runnable类,在workers里面没有空闲worker来处理task的时候,会将task放入这里面。
  2. workers:HashSet。这个集合来存储worker,worker这个类其实是对提交的Runnable类的封装,而且它自己也实现了Runnable接口,它的run方法里面调用的就是Runnable类的run方法。workers里worker的数量,取决于定义的ThreadPool的类型和大小。关于Worker类

  • t.isInterrupted():如果线程已经中断过了,就不用中断了。
  • w.tryLock():这个方法用来判断worker是否空闲。

compareAndSetState(0, 1):比较当前state是否是0(空闲),是0就返回true,然后将state设置为1,否则返回false。
这个方法使用Unsafe的CAS操作保证了原子性。关于Unsafe类
Worker在开始执行task之后,会将state设置为1。

  • w.lock():开始执行task,state设置为1。
  • w.unlock():task执行结束(完成or异常),state设置为0。

lock():调用AQS的acquire,acquire里面调用tryAcquire,就是实现了AQS的子类的tryAcquire,这里就是Worker类。tryAcquire将state设置为1(源码见上面)。关于AQS
判断当前线程未被中断,且worker是空闲状态,调用t.interrupt()。getTask()里面的阻塞方法(读取阻塞队列)会响应这次中断,抛出InterruptedException,跳出while循环,执行processWorkerExit。关于线程中断:《Java并发编程实战》。

  • decrementWorkerCount():减少ThreadPool的容量(worker的最大数量)。
  • workers.remove(w):移除worker。

阻塞队列里面的task虽然还未执行,但是没有被删除掉,所以还是会执行。

2.shutdownNow
已经submit并且执行task会继续执行,如果task里面有阻塞方法,会抛出InterruptedException。已经submit但尚未执行的task,就不会再执行了。

  • advanceRunState(STOP):线程池状态设置为STOP。
  • interruptWorkers():中断所有workers。
  • drainQueue():清除workQueue里面的task。

workers里面所有的worker都进行中断,所以提交的Runnable类中的阻塞方法都会抛出InterruptedException。但是具体要不要中断该线程,就要自己进行处理了。

清除了workQueue里面已经submit但尚未执行的任务,所以这些任务就不会再执行了。

4 Replies to “JUC线程池源码阅读1:ThreadPool的shutdown”

  1. Pingback: buy cialis uk
  2. Pingback: viagra buy thailand
  3. Pingback: ed pills walmart

发表评论