1. Hilo
1. ¿Qué es un hilo?
Thread es la unidad más pequeña que el sistema operativo puede realizar en la programación de cálculos. Está incluido en el proceso y es la unidad operativa real en el proceso. Un subproceso se refiere a un único flujo de control secuencial en un proceso. Se pueden ejecutar varios subprocesos al mismo tiempo en un proceso y cada subproceso realiza diferentes tareas en paralelo.
2. Cómo crear un hilo
2.1 Creando hilos en JAVA
/**
* 继承Thread类,重写run方法
*/
class MyThread extends Thread {
@Override
public void run() {
System.out.println("myThread..." + Thread.currentThread().getName());
} }
/**
* 实现Runnable接口,实现run方法
*/
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable..." + Thread.currentThread().getName());
} }
/**
* 实现Callable接口,指定返回类型,实现call方法
*/
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "MyCallable..." + Thread.currentThread().getName();
} }
2.2 Pruébalo
public static void main(String[] args) throws Exception {
MyThread thread = new MyThread();
thread.run(); //myThread...main
thread.start(); //myThread...Thread-0
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
myRunnable.run(); //MyRunnable...main
thread1.start(); //MyRunnable...Thread-1
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread thread2 = new Thread(futureTask);
thread2.start();
System.out.println(myCallable.call()); //MyCallable...main
System.out.println(futureTask.get()); //MyCallable...Thread-2
}
2.3 Problema
Dado que hemos creado un hilo, ¿por qué los resultados son diferentes cuando llamamos al método directamente y cuando llamamos al método start()? ¿New Thread() realmente crea un hilo?
2.4 Análisis de problemas
Cuando llamamos al método directamente, podemos ver que es el hilo principal de ejecución, y llamar al método start() inicia un nuevo hilo, lo que significa que new Thread() no crea un hilo, sino que crea un hilo en el inicio. ().
Entonces echemos un vistazo al método start() de la clase Thread:
class Thread implements Runnable { //Thread类实现了Runnalbe接口,实现了run()方法
private Runnable target;
public synchronized void start() {
...
boolean started = false;
try {
start0(); //可以看到,start()方法真实的调用时start0()方法
started = true;
} finally {
...
}
}
private native void start0(); //start0()是一个native方法,由JVM调用底层操作系统,开启一个线程,由操作系统过统一调度
@Override
public void run() {
if (target != null) {
target.run(); //操作系统在执行新开启的线程时,回调Runnable接口的run()方法,执行我们预设的线程任务
}
}
}
2.5 Resumen
2. Subprocesos múltiples
1. ¿Qué es el subproceso múltiple?
Multithreading se refiere a la tecnología que permite la ejecución simultánea de múltiples subprocesos desde software o hardware. El mismo hilo solo puede procesar una tarea antes de procesar la siguiente. A veces necesitamos procesar varias tareas al mismo tiempo. En este caso, necesitamos crear varios hilos para procesar las tareas al mismo tiempo.
2. ¿Cuáles son los beneficios del subproceso múltiple?
2.1 Procesamiento en serie
public static void main(String[] args) throws Exception {
System.out.println("start...");
long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
Thread.sleep(2000); //每个任务执行2秒
System.out.println("task done..."); //处理执行结果
}
long end = System.currentTimeMillis();
System.out.println("end...,time = " + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 10043
2.2 Procesamiento paralelo
public static void main(String[] args) throws Exception {
System.out.println("start...");
long start = System.currentTimeMillis();
List<Future> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(2000); //每个任务执行2秒
return "task done...";
}
};
FutureTask task = new FutureTask(callable);
list.add(task);
new Thread(task).start();
}
list.forEach(future -> {
try {
System.out.println(future.get()); //处理执行结果 } catch (Exception e) {
}
});
long end = System.currentTimeMillis();
System.out.println("end...,time = " + (end - start));
}
//执行结果
start...
task done...
task done...
task done...
task done...
task done... end...,time = 2005
2.3 Resumen
2.4 Problemas con subprocesos múltiples
Podemos ver en el ejemplo anterior que si creamos un hilo para cada tarea, si hay muchas tareas, crearemos una gran cantidad de hilos, lo que puede provocar el agotamiento de los recursos del sistema. Al mismo tiempo, sabemos que la ejecución de subprocesos necesita aprovechar los recursos de la CPU. Si hay demasiados subprocesos, se dedicará mucho tiempo a la sobrecarga de cambio de subprocesos.
Además, cada tarea requiere la creación de un hilo, y la creación de un hilo requiere llamar al método subyacente del sistema operativo, lo cual es costoso y el hilo se recicla una vez completado. Cuando se necesita una gran cantidad de subprocesos, crear subprocesos lleva mucho tiempo.
3. Grupo de subprocesos
1. Cómo diseñar un grupo de subprocesos
Dado que el desarrollo de subprocesos múltiples tiene algunos de los problemas anteriores, ¿podemos diseñar algo para evitarlos? ¡Por supuesto! El grupo de subprocesos nació para resolver estos problemas. Entonces, ¿cómo deberíamos diseñar un grupo de subprocesos para resolver estos problemas? En otras palabras, ¿qué funciones debería tener un grupo de subprocesos?
1.1 Funciones básicas del grupo de subprocesos
1.2 El grupo de subprocesos enfrenta problemas
1.3 La innovación viene de la vida
Primero supongamos un escenario: supongamos que somos los gerentes de una empresa de logística. Los bienes a entregar son nuestras tareas y los camiones son nuestras herramientas de entrega. Por supuesto, no podemos preparar tantos camiones como bienes hay. Entonces, cuando los clientes continúan entregándonos productos, ¿cómo debemos gestionarlos para que la empresa pueda funcionar mejor?
1.4 La tecnología proviene de la innovación
Según el escenario anterior, la empresa de logística es nuestro grupo de subprocesos, los productos son nuestras tareas de subprocesos y los camiones son nuestros subprocesos. La forma en que diseñamos el proceso de gestión de camiones de la empresa debería ser la forma en que diseñamos el proceso de gestión del grupo de subprocesos.
2. Análisis detallado del grupo de subprocesos.
Arriba, hablamos sobre cómo diseñar un grupo de subprocesos, ahora veamos cómo lo diseñó el maestro;
2.1 ¿Cómo se diseña el grupo de subprocesos en JAVA?
2.1.1 Diseño del grupo de subprocesos
Eche un vistazo a las propiedades del grupo de subprocesos para comprender el diseño del grupo de subprocesos.
public class ThreadPoolExecutor extends AbstractExecutorService {
//线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//值为29,用来表示偏移量
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池的最大容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池的运行状态,总共有5个状态,用高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 final int TIDYING = 2 << COUNT_BITS; //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法
private static final int TERMINATED = 3 << COUNT_BITS; // terminated()方法已经执行结束
//任务缓存队列,用来存放等待执行的任务
private final BlockingQueue<Runnable> workQueue;
//全局锁,对线程池状态等属性修改时需要使用这个锁
private final ReentrantLock mainLock = new ReentrantLock();
//线程池中工作线程的集合,访问和修改需要持有全局锁
private final HashSet<Worker> workers = new HashSet<Worker>();
// 终止条件
private final Condition termination = mainLock.newCondition();
//线程池中曾经出现过的最大线程数
private int largestPoolSize;
//已完成任务的数量
private long completedTaskCount;
//线程工厂
private volatile ThreadFactory threadFactory;
//任务拒绝策略
private volatile RejectedExecutionHandler handler;
//线程存活时间
private volatile long keepAliveTime;
//是否允许核心线程超时
private volatile boolean allowCoreThreadTimeOut;
//核心池大小,若allowCoreThreadTimeOut被设置,核心线程全部空闲超时被回收的情况下会为0
private volatile int corePoolSize;
//最大池大小,不得超过CAPACITY
private volatile int maximumPoolSize;
//默认的任务拒绝策略
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//运行权限相关
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
...
}
Para resumir: del diseño del grupo de subprocesos anterior, se puede ver que la función del grupo de subprocesos aún es muy completa.
2.1.2 Constructor de grupo de subprocesos
//构造函数
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大允许线程数
long keepAliveTime, //线程存活时间
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //任务缓存队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) { //拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
Para resumir:
2.1.3 Ejecución del grupo de subprocesos
2.1.3.1 Método de enviar tarea
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
Puede ver que el método subyacente del método de envío también llama al método de ejecución, por lo que aquí solo analizamos el método de ejecución;
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//第一步:创建核心线程
if (workerCountOf(c) < corePoolSize) { //worker数量小于corePoolSize
if (addWorker(command, true)) //创建worker
return;
c = ctl.get();
}
//第二步:加入缓存队列
if (isRunning(c) && workQueue.offer(command)) { //线程池处于RUNNING状态,将任务加入workQueue任务缓存队列
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //双重检查,若线程池状态关闭了,移除任务
reject(command);
else if (workerCountOf(recheck) == 0) //线程池状态正常,但是没有线程了,创建worker
addWorker(null, false);
}
//第三步:创建临时线程
else if (!addWorker(command, false))
reject(command);
}
Para resumir: las funciones principales del método ejecutar():
2.1.3.2 Crear hilos
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//等价于:rs>=SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())
//线程池已关闭,并且无需执行缓存队列中的任务,则不创建
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c)) //CAS增加线程数
break 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();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); //这里将线程加入到线程池中
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); //添加成功,启动线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); //添加线程失败操作
}
return workerStarted;
}
Resumen: Las funciones principales del método addWorker();
2.1.3.3 Implementación de subprocesos de trabajo
private final class Worker //Worker类是ThreadPoolExecutor的内部类
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread; //持有实际线程
Runnable firstTask; //worker所对应的第一个任务,可能为空
volatile long completedTasks; //记录执行任务数
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this); //当前线程调用ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
}
...继承AQS,实现了不可重入锁...
}
Resumen: las funciones principales del hilo de trabajo Clase de trabajador;
2.1.3.4 Reutilización de hilos
final void runWorker(Worker w) { //ThreadPoolExecutor中的runWorker方法,在这里实现的线程复用
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) { //这里会不断从任务队列获取任务并执行
w.lock();
//线程是否需要中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task); //执行任务前的Hook方法,可自定义
Throwable thrown = null;
try {
task.run(); //执行实际的任务
} 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); //执行任务后的Hook方法,可自定义
}
} finally {
task = null; //执行完成后,将当前线程中的任务制空,准备执行下一个任务
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); //线程执行完成后的清理工作
}
}
Resumen: Las funciones principales del método runWorker();
2.1.3.5 Obtener tareas a ejecutar de la cola
private Runnable getTask() {
boolean timedOut = false; //标识当前线程是否超时未能获取到task对象
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c)) //若线程存活时间超时,则CAS减去线程数量
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //允许超时回收则阻塞等待
workQueue.take(); //不允许则直接获取,没有就返回null
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
Resumen: Las funciones principales del método getTask();
2.1.3.6 Trabajos de limpieza
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w); //移除执行完成的线程
} finally {
mainLock.unlock();
}
tryTerminate(); //每次回收完一个线程后都尝试终止线程池
int c = ctl.get();
if (runStateLessThan(c, STOP)) { //到这里说明线程池没有终止
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false); //异常终止线程的话,需要在常见一个线程
}
}
Resumen: Las funciones principales del método ProcessWorkerExit();
2.1.3.7 Intente finalizar el grupo de subprocesos
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//若线程池正在执行、线程池已终止、线程池还需要执行缓存队列中的任务时,返回
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//执行到这里,线程池为SHUTDOWN且无待执行任务 或 STOP 状态
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE); //只中断一个线程
return;
}
//执行到这里,线程池已经没有可用线程了,可以终止了
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //CAS设置线程池终止
try {
terminated(); //执行钩子方法
} finally {
ctl.set(ctlOf(TERMINATED, 0)); //这里将线程池设为终态
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
Resumen: Las funciones principales del método tryTerminate();
2.2 Resumen del grupo de subprocesos JAVA
Del análisis detallado anterior del grupo de subprocesos JAVA, podemos ver que aunque el proceso parece complicado, en realidad hay muchos contenidos que incluyen verificación de duplicación de estado, garantía de seguridad de subprocesos, etc. Sus funciones principales son similares al diseño que propusimos anteriormente. las funciones son las mismas, pero se agregan algunas extensiones adicionales.Organicemos brevemente las funciones del grupo de subprocesos;
2.2.1 Funciones principales
2.2.2 Funciones ampliadas
2.2.3 Resumen del proceso
Del análisis anterior del proceso de envío de tareas del grupo de subprocesos JAVA, podemos ver que el proceso simple de ejecución del grupo de subprocesos se muestra en la siguiente figura;
2.3 Uso del grupo de subprocesos JAVA
Uso básico del grupo de subprocesos para verificar el proceso anterior:
public static void main(String[] args) throws Exception {
//创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue(5));
//加入4个任务,小于核心线程,应该只有4个核心线程,队列为0
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 4
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
//再加4个任务,超过核心线程,但是没有超过核心线程 + 缓存队列容量,应该5个核心线程,队列为3
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 5
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 3
//再加4个任务,队列满了,应该5个热核心线程,队列5个,非核心线程2个
for (int i = 0; i < 4; i++) {
threadPoolExecutor.submit(new MyRunnable());
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 7
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
//再加4个任务,核心线程满了,应该5个热核心线程,队列5个,非核心线程5个,最后一个拒绝
for (int i = 0; i < 4; i++) {
try {
threadPoolExecutor.submit(new MyRunnable());
} catch (Exception e) {
e.printStackTrace(); //java.util.concurrent.RejectedExecutionException
}
}
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 10
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 5
System.out.println(threadPoolExecutor.getTaskCount()); //共执行15个任务
//执行完成,休眠15秒,非核心线程释放,应该5个核心线程,队列为0
Thread.sleep(1500);
System.out.println("worker count = " + threadPoolExecutor.getPoolSize()); //worker count = 5
System.out.println("queue size = " + threadPoolExecutor.getQueue().size()); //queue size = 0
//关闭线程池
threadPoolExecutor.shutdown();
}
Multado con 200 yuanes y más de 1 millón de yuanes confiscados You Yuxi: La importancia de los documentos chinos de alta calidad El servidor de migración de núcleo duro de Musk Solon para JDK 21, ¡los hilos virtuales son increíbles! ! ! El control de congestión de TCP salva Internet Flutter para OpenHarmony está aquí El período LTS del kernel de Linux se restaurará de 6 años a 2 años Go 1.22 solucionará el error de la variable del bucle for Svelte construyó una "nueva rueda" - runas Google celebra su 25 aniversarioAutor: Qin Haoran, JD Retail
Fuente: Comunidad de desarrolladores de JD Cloud Indique la fuente al reimprimir