1
头图

Hello everyone, I'm Glacier~~

For the core class ThreadPoolExecutor of the thread pool, what are the important attributes and inner classes that provide important guarantees for the correct operation of the thread pool?

Important properties in the ThreadPoolExecutor class

In the ThreadPoolExecutor class, there are several very important properties and methods. Next, we will introduce these important properties and methods.

ctl related properties

The constant ctl of type AtomicInteger is an important attribute throughout the entire life cycle of the thread pool. It is an atomic class object, which is mainly used to save the number of threads and the state of the thread pool. Let's take a look at the code related to this attribute as shown below.

 //主要用来保存线程数量和线程池的状态,高3位保存线程状态,低29位保存线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程池中线程的数量的位数(32-3)
private static final int COUNT_BITS = Integer.SIZE - 3;
//表示线程池中的最大线程数量
//将数字1的二进制值向右移29位,再减去1
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
//线程池的运行状态
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 final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
//获取线程状态
private static int runStateOf(int c)     { return c & ~CAPACITY; }
//获取线程数量
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static boolean runStateLessThan(int c, int s) {
    return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}
private boolean compareAndIncrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect + 1);
}
private boolean compareAndDecrementWorkerCount(int expect) {
    return ctl.compareAndSet(expect, expect - 1);
}
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}

The description of each state of the thread pool is as follows.

  • RUNNING: Running state, can receive newly submitted tasks, and can also process tasks in the blocking queue
  • SHUTDOWN: Shutdown state, can no longer receive newly submitted tasks, but can process tasks that have been saved in the blocking queue. When the thread pool is in the RUNNING state, calling the shutdown() method will cause the thread pool to enter this state
  • STOP: Cannot receive new tasks, and cannot process tasks that have been saved in the blocking queue, it will interrupt the thread that is processing the task. If the thread pool is in the RUNNING or SHUTDOWN state, calling the shutdownNow() method will cause the thread pool to enter this state
  • TIDYING: If all tasks have been terminated, the effective number of threads is 0 (the blocking queue is empty, and the number of worker threads in the thread pool is 0), the thread pool will enter this state.
  • TERMINATED: The thread pool in the TIDYING state calls the terminated () method, and the thread pool will be used to enter this state

You can also follow the annotations of the ThreadPoolExecutor class to summarize the transitions between the states of the thread pool as shown in the following figure.

  • RUNNING -> SHUTDOWN: The shutdown() method is explicitly called, or the finalize() method is implicitly called
  • (RUNNING or SHUTDOWN) -> STOP: explicitly call the shutdownNow() method
  • SHUTDOWN -> TIDYING: When the thread pool and task queue are both empty
  • STOP -> TIDYING: when the thread pool is empty
  • TIDYING -> TERMINATED: when the terminated() hook method is executed

Other important properties

In addition to ctl-related properties, some other important properties in the ThreadPoolExecutor class are shown below.

 //用于存放任务的阻塞队列  
private final BlockingQueue<Runnable> workQueue;
//可重入锁
private final ReentrantLock mainLock = new ReentrantLock();
//存放线程池中线程的集合,访问这个集合时,必须获得mainLock锁
private final HashSet<Worker> workers = new HashSet<Worker>();
//在锁内部阻塞等待条件完成
private final Condition termination = mainLock.newCondition();
//线程工厂,以此来创建新线程
private volatile ThreadFactory threadFactory;
//拒绝策略
private volatile RejectedExecutionHandler handler;
//默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

Important inner class in ThreadPoolExecutor class

In the ThreadPoolExecutor class, there are inner classes that are crucial to the execution of the thread pool, the Worker inner class and the rejection policy inner class. Next, we look at these inner classes separately.

Worker inner class

From the source code point of view, the Worker class implements the Runnable interface, indicating that it is essentially a thread used to execute tasks. Next, let's look at the source code of the Worker class, as shown below.

 private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    private static final long serialVersionUID = 6138294804551838833L;
    //真正执行任务的线程
    final Thread thread;
    //第一个Runnable任务,如果在创建线程时指定了需要执行的第一个任务
    //则第一个任务会存放在此变量中,此变量也可以为null
    //如果为null,则线程启动后,通过getTask方法到BlockingQueue队列中获取任务
    Runnable firstTask;
    //用于存放此线程完全的任务数,注意:使用了volatile关键字
    volatile long completedTasks;
    
    //Worker类唯一的构造放大,传递的firstTask可以为null
    Worker(Runnable firstTask) {
        //防止在调用runWorker之前被中断
        setState(-1);
        this.firstTask = firstTask;
        //使用ThreadFactory 来创建一个新的执行任务的线程
        this.thread = getThreadFactory().newThread(this);
    }
    //调用外部ThreadPoolExecutor类的runWorker方法执行任务
    public void run() {
        runWorker(this);
    }

    //是否获取到锁 
    //state=0表示锁未被获取
    //state=1表示锁被获取
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }

    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

In the constructor of the Worker class, it can be seen that the synchronization state state is first set to -1, which is set to -1 to prevent the runWorker method from being interrupted before running. This is because if other threads call the shutdownNow() method of the thread pool, if the value of the state state in the Worker class is greater than 0, the thread will be interrupted, and if the value of the state state is -1, the thread will not be interrupted.

The Worker class implements the Runnable interface and needs to rewrite the run method. The run method of the Worker essentially calls the runWorker method of the ThreadPoolExecutor class. In the runWorker method, the unlock method will be called first, which will set the state to 0, so At this time, calling the shutdownDownNow method will interrupt the current thread, and the runWork method has been entered at this time, so the thread will not be interrupted before the runWorker method is executed.

Note: You need to focus on understanding the implementation of the Worker class.

Deny policy inner class

In the thread pool, if the workQueue blocking queue is full and there is no idle thread pool, at this time, to continue submitting tasks, a strategy needs to be adopted to process this task. The thread pool provides a total of four strategies, as shown below.

  • Throwing an exception directly is also the default strategy. The implementation class is AbortPolicy.
  • Use the thread where the caller is located to execute the task. The implementation class is CallerRunsPolicy.
  • Discard the top task in the queue and execute the current task. The implementation class is DiscardOldestPolicy.
  • Directly discard the current task. The implementation class is DiscardPolicy.

Four inner classes are provided in the ThreadPoolExecutor class to implement the corresponding policies by default, as shown below.

 public static class CallerRunsPolicy implements RejectedExecutionHandler {

    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

public static class AbortPolicy implements RejectedExecutionHandler {

    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
    }
}

public static class DiscardPolicy implements RejectedExecutionHandler {

    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

public static class DiscardOldestPolicy implements RejectedExecutionHandler {

    public DiscardOldestPolicy() { }


    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

We can also customize the rejection policy by implementing the RejectedExecutionHandler interface and rewriting the rejectedExecution method of the RejectedExecutionHandler interface. When creating a thread pool, call the constructor of ThreadPoolExecutor and pass in our own rejection policy.

For example, a custom deny policy is shown below.

 public class CustomPolicy implements RejectedExecutionHandler {

    public CustomPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            System.out.println("使用调用者所在的线程来执行任务")
            r.run();
        }
    }
}

Create a thread pool with a custom deny policy.

 new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                       60L, TimeUnit.SECONDS,
                       new SynchronousQueue<Runnable>(),
                       Executors.defaultThreadFactory(),
               new CustomPolicy());

Let's stop here today, I'm Glacier, see you next time~~


冰河
156 声望970 粉丝