Talk about the java thread pool ThreadPoolExecutor (two)-thread pool status

We know that once a task comes, the thread pool will allocate threads to process the task, or put the task in the cache queue. So how does the thread pool handle tasks efficiently? Multiple tasks enter the cache queue, and multithreads fetch tasks. How does this deal with concurrency issues? Threads are stateful, so is the thread pool stateful? Let's take a look at it bit by bit.

First of all, let's talk about the basic thread pool state attributes.

Thread pool state: There are five states in the thread pool, and these five states are encapsulated to a certain extent. They are:

  • RUNNING: Can accept newly submitted tasks, and can also process tasks in the blocking queue, which is running.
  • SHUTDOWN: Closed state, no longer accepting newly submitted tasks, but you can continue to process the saved tasks 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. I found it useful in the code in the previous article.
  • STOP: Can not accept new tasks, do not process tasks in the queue, and interrupt the thread that is processing tasks. When the thread pool is in the RUNNING or SHUTDOWN state, calling the shutdownNow() method will make the thread pool enter this state. It is best not to call this directly. Some tasks will not be processed, and an exception will be thrown.
  • TIDYING: If all tasks have been terminated, workerCount (the number of effective threads) is 0, and the thread pool will call the terminated() method to enter the TERMINATED state after entering this state.
  • TERMINATED: Enter this state after the terminated() method is executed. By default, nothing is done in the terminated() method.

Let's take a look at the code:

   private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
     // 计数位 29
	private static final int COUNT_BITS = Integer.SIZE - 3;
	// 左移29位然后-1,0001 1111 1111 1111 1111 1111 1111 1111
	private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
	
	// 五种线程状态
	//-1左移29位,也就是-536870912
	//二进制展示是1010 0000 0000 0000 0000 0000 0000 0000
    private static final int RUNNING    = -1 << COUNT_BITS;
	//二进制展示是0000 0000 0000 0000 0000 0000 0000 0000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
	//二进制展示是0010 0000 0000 0000 0000 0000 0000 0000
    private static final int STOP       =  1 << COUNT_BITS;
	//二进制展示是0100 0000 0000 0000 0000 0000 0000 0000
    private static final int TIDYING    =  2 << COUNT_BITS;
	//二进制展示是0110 0000 0000 0000 0000 0000 0000 0000
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 获取线程状态
	//~代表非,那么~CAPACITY也就是1110 0000 0000 0000 0000 0000 0000 0000
	// c 假设是                   1010 0000 0000 0000 0000 0000 0000 0000
	// 那么返回的就是             1010 0000 0000 0000 0000 0000 0000 0000
    private static int runStateOf(int c)     { return c & ~CAPACITY; }

The thread pool uses ctl to control the running status of the thread pool and the number of effective threads in the thread pool. The thread-safe AtomicInteger is used. Ctl has a total of 32 bits. The first three bits are the status bits, and the last 29 bits are the count of the number of threads. That is 2^29-1=536870912, which is more than 500 million, which is a lot. If it exceeds so many threads, it is estimated that it will not hold it. It should not be used so much. The CPU is GG, O(∩_∩)O haha ~.

We can see that the state of the thread is represented by a 29-digit number shifted to the left by -1,0,1,2,3. Note: It is never -1,0,1,2,3. If you say so, you know you I haven't read the source code. I glanced at it and started to talk. . Thief embarrassed

Then, when getting the status, it can be obtained directly through c & ~CAPACITY, and the quantity can also be obtained directly through ctl. In this way, is the design a good sample paper? Hehe

Let's take a look at the operation.

    public void execute(Runnable command) {
	    // 运行的对象都没有,只能抛出异常了
        if (command == null)
            throw new NullPointerException();
		// 获取ctl的value,线程状态和workerCount都在里面,没它不行啊
        int c = ctl.get();
		//上面说了获取线程状态。workerCountOf(c) 这个就是获取线程数量了
		//小于核心线程数,那就分配线程直接运行呗
        if (workerCountOf(c) < corePoolSize) {
			// 添加到任务
            if (addWorker(command, true))
                return;
			//添加任务失败,这个时候ctl也许会改变,需要重新获取ctl
            c = ctl.get();
        }
		//如果当前线程是运行状态并且添加任务到列表成功
        if (isRunning(c) && workQueue.offer(command)) {
			// 获取ctl
            int recheck = ctl.get();
			//再次判断线程池的运行状态,如果不是运行状态,把刚刚加到workQueue中的command移除
            if (! isRunning(recheck) && remove(command))
				// 拒绝策略
                reject(command);
			//有效线程数为0,那就要执行addWork方法了
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
		//再次调用addWorker方法,但第二个参数传入为false,
		//将线程池的有限线程数量的上限设置为maximumPoolSize;
        else if (!addWorker(command, false))
            reject(command);
    }

The above is the main method of executing tasks running in the thread pool, and a simple comment has been written. This code is mainly divided into three steps.

  1. If the number of running threads is less than corePoolSize, directly use threads to perform tasks
  2. If the task is queued successfully, then it is still necessary to check whether it should join the thread, because the thread that was just checked may have been executed.
  3. If you cannot enter the cache queue, the cache queue is full and you can only restart the thread. If the maximum thread allows, if the thread fails to start, the task can only be rejected.

Let's look at the core method addWorker~

    private boolean addWorker(Runnable firstTask, boolean core) {
		//retry 这个是标志位,就是在循环中continue,break的时候可以进行多层跳出
		//个人觉得这段代码使用while处理可能更亲切一点,哈哈
        retry:
		// 无限循环,不多说
        for (;;) {
			//获取ctl以及状态
            int c = ctl.get();
            int rs = runStateOf(c);

            // 线程池的状态是int类型,从运行到关闭是逐渐增大的,所以直接使用大于小于对比
			// rs >= SHUTDOWN,表示此时不再接收新任务
			// rs == SHUTDOWN,这时表示关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
		    // 既然不接受新任务,那 firstTask当然要为空
			// 如果workQueue不为空,就是处理workQueue里面的任务,没问题,但是加了一个否定,那还处理啥?
            if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
                return false;

            for (;;) {
				// 工作的线程的计数,就是第几个工作的线程
                int wc = workerCountOf(c);
				// 超出最大限制(默认2亿多,这个是可以初始化设置的,不会真是这么多)还搞啥?GG,
				// 根据core判断,是取corePoolSize还是maximumPoolSize,超过了线程数目,当然有也GG
                if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
				// 尝试增加workerCount,成功的话就跳出循环
                if (compareAndIncrementWorkerCount(c))
					//跳出retry下面的这个循环
                    break retry;
				// 重新获取ctl	
                c = ctl.get(); 
				// 获取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 {
			// 新建一个worker
            w = new Worker(firstTask);
			// 获取worker的线程
			//这个ThreadFactory如果没设置的话,会默认使用默认java.util.concurrent.Executors.DefaultThreadFactory
            final Thread t = w.thread;
	        //线程肯定不能为空,为空的话那就增加任务失败,执行不了呗
            if (t != null) {
				//获取新的ReentrantLock锁(排它锁)
                final ReentrantLock mainLock = this.mainLock;
				//加锁,不多说哈,不熟悉的可以看下之前锁的讲解
				// 因为这是一个线程池,肯定存在多线程竞争的情况,比如同时去取一个任务,同时去执行等等
                mainLock.lock();
                try {
                    //获取ctl状态
                    int rs = runStateOf(ctl.get());
					// rs < SHUTDOWN表示是RUNNING
					// 如果rs是RUNNING状态或者rs是SHUTDOWN状态并且firstTask为null,继续下面的逻辑
					// SHUTDOWN状态下,不会添加新的任务,只会执行缓存列表中的任务
                    if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {
						//预检查t是否可启动
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
						// private final HashSet<Worker> workers = new HashSet<Worker>();
						// workers是一个HashSet
                        workers.add(w);
						// largestPoolSize 线程池的最大线程数量
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
				// 任务增加成功,也分配到线程了
                if (workerAdded) {
				    // 那就启动线程,运行任务呗
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
			// 启动失败的话,就把已经加入到workers里面的任务移除掉
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

addWorker has only two input parameters, firstTask and core, that is, addworker (add task) only cares about the size of the task and thread pool, and the size of the thread pool is determined according to the parameters passed by the core.

To enter this method, the first is to filter the state. The thread pool has 5 states. Except for RUNNING and SHOWDOWN, other states will no longer execute tasks, and the SHUTDOWN state only executes tasks in the cache queue, so addWorker first Judged this.

Next, it is the judgment of the number of threads. The number of threads must not exceed the maximum number of threads allowed by the method. If it exceeds the maximum number of threads, no thread can handle the task, that is, it will directly GG and return false.

Of course, if the state changes in the middle of the execution, for example, calling shutdown or calling the shutdownNow method, you have to go through the for loop again.

Now that the above checks are all right, we will start adding tasks.

Create a new task, acquire a thread, and then add a lock, an exclusive lock. Because the thread pool is multi-threaded, it exists and finds elephants, and the HashSet of workers is not thread-safe, so it needs to be locked.

After the lock is added, the worker is successfully added to the workers, which means that the task is successfully added, and it can be started afterwards.

When starting, it is directly t.start(). You can see how this t came from. t is taken from the worker, the construction method of the worker

Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
    this.thread = getThreadFactory().newThread(this); 这个线程中的runnable,其实就是传进来的firstTask!所以t.strat(),也就是运行firstTask。
    大致的流程就是这样哈。

      It's almost done, let's see it again next time~

No sacrifice,no victory~

 

Guess you like

Origin blog.csdn.net/zsah2011/article/details/110230276