ThreadPool Executor for Learning Concurrent Programming in java

Keywords: Java less

ThreadPoolExecutor

ThreadPoolExecutor is the parent of all thread pool implementations. Let's first look at the constructor

Constructional parameters

  • corePoolSize: Number of Thread Cores
  • Maximum PoolSize: Maximum number of threads
  • Keep AliveTime: When a thread is idle, it will only survive if the number of threads is greater than corePoolSize
  • Unit: unit of survival time
  • workQueue: Blocking queue for tasks
  • threadFactory: The project of creating threads, naming threads
  • handler: When the thread pool is full, you can also customize the strategy for selecting new tasks, such as throwing exceptions, discarding current tasks, discarding the oldest tasks in the blocking queue, etc.

Technological process

  1. Determine whether the number of corePoolSize threads exceeds the number of corePoolSize threads created
  2. If the number of threads exceeds the core number, the queue will be filled or not and put into the queue.
  3. The queue is full, and if it exceeds maximumPoolSize, create threads without
  4. Overtake, execute according to strategy

Source code parsing

//32. The first three bits are the state of the thread pool, and the last three are the number of threads.
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;//28
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;00011111 11111111 11111111 11111110
//- The binary of 1 is 1111111111 11111 11111 11111 11111 11111 11111 11 11111 11
private static final int RUNNING    = -1 << COUNT_BITS;//- As mentioned above, after moving 28 bits to the left, it will be 111 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
private static final int SHUTDOWN   =  0 << COUNT_BITS;//0 moved 28 bits left, or 0,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000
private static final int STOP       =  1 << COUNT_BITS;//00100000 00000000 00000000 00000000
private static final int TIDYING    =  2 << COUNT_BITS;//01000000 00000000 00000000 00000000
private static final int TERMINATED =  3 << COUNT_BITS;//01100000 00000000 00000000 00000000
private static int runStateOf(int c)     { return c & ~CAPACITY; }//~ CAPACITY is 1100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000.
private static int workerCountOf(int c)  { return c & CAPACITY; }//And finish, is the number of threads
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static boolean isRunning(int c) {
    return c < SHUTDOWN;//Less than 0, RUNNING, RUNNING=-1
}

execute method

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {//If the number of threads is less than the number of thread cores
        if (addWorker(command, true))//Increase task success, return to true, unsuccessful, continue
            return;
        c = ctl.get();
    }
    //Judgment queue
    if (isRunning(c) && workQueue.offer(command)) {//If the thread pool is still running and can be inserted into the queue
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))//Thread pool is not running, remove the task just inserted
            reject(command);//Implementation strategy
        else if (workerCountOf(recheck) == 0)//
            addWorker(null, false);
    }
    //The queue is full, judging the maximum number of threads
    else if (!addWorker(command, false))
        reject(command);//Implementation strategy
}

addWorker method

private boolean addWorker(Runnable firstTask, boolean core) {//core is true, use corePoolSize to judge, otherwise use maximumPoolSize
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);//Get the current thread state

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && // It's STOP, TIDYING, TERMINATED. No tasks are allowed in at this time.
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))//
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;//No more threads than the core or maximum number of threads
            if (compareAndIncrementWorkerCount(c))//Return true, indicating success, and jump out of the retry loop
                break retry;
            //Failed, indicating that the thread is occupied by other symbolic conditions, and then determine whether the thread state is the same as before, different retrieve, jump to retry
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();//Acquisition locks
            try {
                int rs = runStateOf(ctl.get());//Get the state of the thread pool

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // run is not started by start
                        throw new IllegalThreadStateException();
                    workers.add(w);//Additional hashset
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;//Update the current maximum
                    workerAdded = true;//Increase success
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();//Start threads
                workerStarted = true;//Successful start-up
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);//Failure, thread count - 1, remove from hashset, and try Terminate
    }
    return workerStarted;
}

runWorker Method

When t.start(); is executed above, the following method is called through the run method

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {//Tasks are not empty or acquired tasks are not empty.
            w.lock();           
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();//Call the run method, which does not pass start, that is, no new thread is started
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;//Number of tasks completed plus 1
                w.unlock();//release
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);//Remove w, when task is empty, such as when thread pool status stops or too many threads are started
    }
}

getTask method
When Worker first starts, it calls the run method, and then it gets tasks from the queue all the time.

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);//Get the current thread pool status

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {//
            decrementWorkerCount();//Number of threads - 1
            return null;
        }

        int wc = workerCountOf(c);//Number of threads
        //allowCoreThreadTimeOut is true, indicating that the number of threads should be determined by whether the number of keepAliveTime threads exceeds the number of core threads.
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//Does it exceed the number of core threads?

        if ((wc > maximumPoolSize || (timed && timedOut))//Over the maximum number of threads
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))//Number of threads - 1
                return null;//Return empty
            continue;
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();//Acquisition task
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

Posted by FuzziNectar on Wed, 31 Jul 2019 02:45:03 -0700