[Alta concurrencia] No cause problemas, así es como el grupo de subprocesos logra una salida elegante

Hola a todos, soy Glacier~~

En [Temas de alta concurrencia], analizamos en profundidad las interfaces importantes y las clases abstractas en el grupo de subprocesos desde la perspectiva del código fuente, cómo se crea el grupo de subprocesos, qué atributos y clases internas tiene la clase ThreadPoolExecutor y su impacto en el grupo de subprocesos. papel importante. Analiza en profundidad el proceso central general del grupo de subprocesos y cómo desensamblar el código de ejecución del subproceso de trabajo, y analiza en profundidad el proceso de ejecución del subproceso de trabajo.

En este artículo, analizaremos en profundidad cómo el grupo de subprocesos sale del programa con gracia desde la perspectiva del código fuente. Primero, echemos un vistazo al método shutdown() en la clase ThreadPoolExecutor.

método de apagado ()

Al usar el grupo de subprocesos, después de llamar al método shutdown(), el grupo de subprocesos ya no aceptará nuevas tareas de ejecución. Sin embargo, las tareas colocadas en la cola de tareas antes de que se llame al método shutdown() aún se ejecutarán. Este método es un método sin bloqueo y regresará inmediatamente después de ser llamado, en lugar de esperar a que se ejecuten todas las tareas en la cola de tareas antes de regresar. Echemos un vistazo al código fuente del método shutdown(), como se muestra a continuación.

public void shutdown() {
	//获取线程池的全局锁
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭线程池的权限
		checkShutdownAccess();
		//将当前线程池的状态设置为SHUTDOWN
		advanceRunState(SHUTDOWN);
		//中断Worker线程
		interruptIdleWorkers();
		//为ScheduledThreadPoolExecutor调用钩子函数
		onShutdown(); // hook for 
	} finally {
		//释放线程池的全局锁
		mainLock.unlock();
	}
	//尝试将状态变为TERMINATED
	tryTerminate();
}

En general, el código del método shutdown() es relativamente simple. Primero, verifique si tiene permiso para cerrar el grupo de subprocesos. Si tiene permiso, verifique nuevamente si tiene permiso para interrumpir el subproceso de trabajo. Si no tiene permiso, se lanzará una SecurityException. El código se muestra a continuación.

//检查是否有关闭线程池的权限
checkShutdownAccess();
//将当前线程池的状态设置为SHUTDOWN
advanceRunState(SHUTDOWN);
//中断Worker线程
interruptIdleWorkers();

Entre ellos, el código de implementación del método checkShutdownAccess() es el siguiente.

private void checkShutdownAccess() {
	SecurityManager security = System.getSecurityManager();
	if (security != null) {
		security.checkPermission(shutdownPerm);
		final ReentrantLock mainLock = this.mainLock;
		mainLock.lock();
		try {
			for (Worker w : workers)
				security.checkAccess(w.thread);
		} finally {
			mainLock.unlock();
		}
	}
}

El código del método checkShutdownAccess() es relativamente simple de entender, es decir, para detectar si tiene permiso para cerrar el grupo de subprocesos, durante el cual se utiliza el bloqueo global del grupo de subprocesos.

A continuación, observamos el código fuente del método advanceRunState(int) como se muestra a continuación.

private void advanceRunState(int targetState) {
	for (;;) {
		int c = ctl.get();
		if (runStateAtLeast(c, targetState) ||
			ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
			break;
	}
}

La lógica general del método advanceRunState(int) es juzgar si el estado del conjunto de subprocesos actual es el estado especificado. El estado que se pasa en el método shutdown() es SHUTDOWN. Si es SHUTDOWN, regresa directamente; si es no APAGADO, el subproceso actual El estado del grupo se establece en APAGADO.

A continuación, observamos el método interruptIdleWorkers() llamado por el método showdown() como se muestra a continuación.

private void interruptIdleWorkers() {
	interruptIdleWorkers(false);
}

可以看到,interruptIdleWorkers()方法调用的是interruptIdleWorkers(boolean)方法,继续看interruptIdleWorkers(boolean)方法的源代码,如下所示。

private void interruptIdleWorkers(boolean onlyOne) {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		for (Worker w : workers) {
			Thread t = w.thread;
			if (!t.isInterrupted() && w.tryLock()) {
				try {
					t.interrupt();
				} catch (SecurityException ignore) {
				} finally {
					w.unlock();
				}
			}
			if (onlyOne)
				break;
		}
	} finally {
		mainLock.unlock();
	}
}

上述代码的总体逻辑为:获取线程池的全局锁,循环所有的工作线程,检测线程是否被中断,如果没有被中断,并且Worker线程获得了锁,则执行线程的中断方法,并释放线程获取到的锁。此时如果onlyOne参数为true,则退出循环。否则,循环所有的工作线程,执行相同的操作。最终,释放线程池的全局锁。

接下来,我们看下shutdownNow()方法。

shutdownNow()方法

如果调用了线程池的shutdownNow()方法,则线程池不会再接受新的执行任务,也会将任务队列中存在的任务丢弃,正在执行的Worker线程也会被立即中断,同时,方法会立刻返回,此方法存在一个返回值,也就是当前任务队列中被丢弃的任务列表。

shutdownNow()方法的源代码如下所示。

public List<Runnable> shutdownNow() {
	List<Runnable> tasks;
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		//检查是否有关闭权限
		checkShutdownAccess();
		//设置线程池的状态为STOP
		advanceRunState(STOP);
		//中断所有的Worker线程
		interruptWorkers();
		//将任务队列中的任务移动到tasks集合中
		tasks = drainQueue();
	} finally {
		mainLock.unlock();
	}
	/尝试将状态变为TERMINATED
	tryTerminate();
	//返回tasks集合
	return tasks;
}

shutdownNow()方法的源代码的总体逻辑与shutdown()方法基本相同,只是shutdownNow()方法将线程池的状态设置为STOP,中断所有的Worker线程,并且将任务队列中的所有任务移动到tasks集合中并返回。

可以看到,shutdownNow()方法中断所有的线程时,调用了interruptWorkers()方法,接下来,我们就看下interruptWorkers()方法的源代码,如下所示。

private void interruptWorkers() {
	final ReentrantLock mainLock = this.mainLock;
	mainLock.lock();
	try {
		for (Worker w : workers)
			w.interruptIfStarted();
	} finally {
		mainLock.unlock();
	}
}

interruptWorkers()方法的逻辑比较简单,就是获得线程池的全局锁,循环所有的工作线程,依次中断线程,最后释放线程池的全局锁。

在interruptWorkers()方法的内部,实际上调用的是Worker类的interruptIfStarted()方法来中断线程,我们看下Worker类的interruptIfStarted()方法的源代码,如下所示。

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

发现其本质上调用的还是Thread类的interrupt()方法来中断线程。

awaitTermination(long, TimeUnit)方法

当线程池调用了awaitTermination(long, TimeUnit)方法后,会阻塞调用者所在的线程,直到线程池的状态修改为TERMINATED才返回,或者达到了超时时间返回。接下来,我们看下awaitTermination(long, TimeUnit)方法的源代码,如下所示。

public boolean awaitTermination(long timeout, TimeUnit unit)
	throws InterruptedException {
	//获取距离超时时间剩余的时长
	long nanos = unit.toNanos(timeout);
	//获取Worker线程的的全局锁
	final ReentrantLock mainLock = this.mainLock;
	//加锁
	mainLock.lock();
	try {
		for (;;) {
			//当前线程池状态为TERMINATED状态,会返回true
			if (runStateAtLeast(ctl.get(), TERMINATED))
				return true;
			//达到超时时间,已超时,则返回false
			if (nanos <= 0)
				return false;
			//重置距离超时时间的剩余时长
			nanos = termination.awaitNanos(nanos);
		}
	} finally {
		//释放锁
		mainLock.unlock();
	}
}

上述代码的总体逻辑为:首先获取Worker线程的独占锁,后在循环判断当前线程池是否已经是TERMINATED状态,如果是则直接返回true,否则检测是否已经超时,如果已经超时,则返回false。如果未超时,则重置距离超时时间的剩余时长。接下来,进入下一轮循环,再次检测当前线程池是否已经是TERMINATED状态,如果是则直接返回true,否则检测是否已经超时,如果已经超时,则返回false。如果未超时,则重置距离超时时间的剩余时长。以此循环,直到线程池的状态变为TERMINATED或者已经超时。

好了,今天就到这儿吧,我是冰河,我们下期见~~

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿

Supongo que te gusta

Origin juejin.im/post/7121532659200688141
Recomendado
Clasificación