线程池ThreadPoolExecutor底层原理源码分析(下)

72 阅读3分钟

线程池源码的基础属性和方法

在线程池的源码中,会通过一个AtomicInteger类型的变量ctl,来表示线程池的状态和当前线程池中的工作线程数。 一个Integer占4个字节,也就是32bit,对应线程池的五种状态:

  1. RUNNING

可以接收新的任务,同时可以去处理队列中的任务

  1. SHUTDOWN

不能接收新的任务,但是会去处理队列中的任务----优雅的关闭线程池(处理完剩余的任务)

  1. STOP

不能接收新的任务,且不能处理队列中的任务,并且中断处理中的任务(一个任务能不能被中断得看任务本身)

  1. TIDYING

所有的任务都结束了,线程池中也没有线程了,状态将转到Terminated状态,一旦到达此状态,就会调用线程池的terminated()--这个方法可以自定义,执行完这个方法之后线程池的状态就会变成Terminated状态 terminated()方法执行完成之后就会转变为TERMINATED

All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method

  1. TERMINATED

terminated() has completed The numerical order among these values matters, to allow ordered comparisons

只要线程池中的状态变成了Terminated状态,线程池中就没有线程了,如果线程池的状态是Terminated,表示线程池已经执行terminated方法了。

2个bit能表示4种状态(00,01,11,10),那5种状态至少需要3个bit位,在线程池的源码中是这么表示的。

private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP  = 1 << COUNT_BITS;
private static int TIDYING = 2 << COUNT_BITS;
private static int TERMINATED = 3 << COUTN_BITS;

Integer.SIZE是一个Java的静态字段,它返回一个整数类型(int)的大小,即在内存中所占用的位数(bit)。在Java中,Integer.SIZE的值始终为32,表示一个int类型占用32个位。

需要注意的是,32位并不是int类型的实际大小(字节数),而是表示使用32个二进制位来存储一个整数值。在Java中,int类型的大小是固定的4个字节(32位),因此实际上是占用4个字节的存储空间。

这样设计的好处是,可以在不同的平台和系统上保持int类型的大小一致,确保代码的可移植性。

Integer.SIZE 为32 ,所以COUNT_BITS为29,最终各个状态对应的二进制为:

  1. RUNNING:11100000 00000000 00000000 00000000
  2. SHUTDOWN:00000000 00000000 00000000 00000000
  3. STOP:00100000 00000000 00000000 00000000
  4. TIDYING:01000000 00000000 00000000 00000000
  5. TERMINATED:01100000 00000000 00000000 00000000

因此只需要使用一个Integer的最高三个bit,就可以表示线程池的5种状态,而剩下的29位表示工作线程数。

一个线程在执行任务的时候被中断了,在获取下一个任务的时候,发现线程池的状态为SHUTDOWN状态,这个时候线程退出。 线程被阻塞时候中断线程池-----抛出异常退出

shutdownNow方法中断所有的线程,shutdown方法中断空闲的线程----tryLock判断是否是中断的线程,能够加到锁表示线程没有在执行任务-----线程是空闲的(shutdown会留一部分线程执行队列中的任务)。线程池中的线程在执行任务的时候会加锁,加锁去执行任务,如果说拿不到锁,说明没拿到锁的线程空闲。