Maison >Java >javaDidacticiel >Explication détaillée des principes du pool de threads et de l'exécuteur en Java
Cet article présente principalement des informations pertinentes qui expliquent en détail l'analyse du pool de threads Java et les principes d'Executor. Des exemples et des principes d'analyse sont fournis ici pour aider tout le monde à comprendre cette partie des connaissances. Les amis dans le besoin peuvent se référer à
Analyse détaillée du pool de threads Java et des principes d'Executor
Fonction du pool de threads et connaissances de base
Réduire la consommation provoquée par la création et la destruction des threads
Mise en œuvre plus pratique et transparente du contrôle des ressources informatiques
Pool de threads mis en cache
ThreadPool corrigé
ThreadPool planifié
1. Implémentation du ThreadPool mis en cache et du ThreadPool fixe
ThreadPoolExecutor executor1 = (ThreadPoolExecutor)Executors.newCachedThreadPool();
ThreadPoolExecutor executor2 = (ThreadPoolExecutor)Executors.newFixedThreadPool(4);
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
maximumPoolSize est le nombre maximum de threads dans le pool de threads ; pour ThreadPool mis en cache, cette valeur est Integer.MAX_VALUE, ce qui est fondamentalement équivalent à l'infini. Quel type de machine peut exécuter des milliards de threads ! ! Pour ThreadPool fixe, cette valeur correspond au nombre de pools de threads définis par l'utilisateur.
keepAliveTime et unit déterminent le délai d'expiration du cache du thread ; pour le ThreadPool mis en cache, le délai d'expiration du cache du thread est d'une minute. En d'autres termes, si un thread de travail n'a rien à faire pendant une minute, il sera révoqué. pour économiser de l'argent. Le temps passé au ThreadPool fixe est de 0, ce qui signifie que le thread de travail dans le ThreadPool fixe n'expirera jamais.corePoolSize是线程池的最小线程数;对于cached ThreadPool,这个值为0,因为在完全没有任务的情况下,cached ThreadPool的确会成为“光杆司令”。至于fixed ThreadPool,这个fixed已经表明corePoolSize是等于线程总数的。
接下来,我们根据一个简单的使用例子,来看看一下cached ThreadPool的流程。
public class Task implements Callable<String> { private String name; public Task(String name) { this.name = name; } @Override public String call() throws Exception { System.out.printf("%s: Starting at : %s\n", this.name, new Date()); return "hello, world"; } public static void main(String[] args) { ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newCachedThreadPool(); Task task = new Task("test"); Future<String> result = executor.submit(task); try { System.out.printf("%s\n", result.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); System.out.printf("Main ends at : %s\n", new Date()); } }
首先,来看看executor.submit(task),这其实调用了ThreadPoolExecutor.execute(Runnable command)方法,这个方法的代码如下,整段代码的逻辑是这样的。首先检查线程池的线程数是否不够corePoolSize,如果不够就直接新建线程并把command添加进去;如果线程数已经够了或者添加失败(多个线程增加添加的情况),就尝试把command添加到队列中(workQueue.offer(command)),如果添加失败了,就reject掉cmd。大体的逻辑是这样的,这段代码有很多基于线程安全的设计,这里为了不跑题,就先忽略细节了。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }
到这里,看起来线程池实现的整体思路其实也没多么复杂。但是还有一个问题——一个普通的Thread在执行完自己的run方法后会自动退出。那么线程池是如何实现Worker线程不断的干活,甚至在没有任务的时候。其实答案很简单,就是Worker其实在跑大循环,Worker实际运行方法如下:
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) { w.lock(); /***/ try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); /***/ } finally { afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
关键就在这个while的判断条件,对于需要cached线程的情况下,getTask()会阻塞起来,如果缓存的时间过期,就会返回一个null,然后Worker就退出了,也就结束了它的服役周期。而在有任务的情况下,Woker会把task拿出来,然后调用task.run()执行任务,并通过Future通知客户线程(即future.get()返回)。这样一个简单的线程池使用过程就完了。。。
当然,线程池的很多精髓知识——基于线程安全的设计,我都没有分析。有兴趣可以自己分析一下,也可以和我讨论。此外Scheduled ThreadPool这里也没有分析,它的要点其实是调度,主要是根据时间最小堆来驱动的。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!