Heim Java javaLernprogramm Detaillierte Erläuterung der Prinzipien von Thread-Pool und Executor in Java

Detaillierte Erläuterung der Prinzipien von Thread-Pool und Executor in Java

Jul 30, 2017 am 10:43 AM
executor java 原理

In diesem Artikel werden hauptsächlich relevante Informationen vorgestellt, die die Analyse des Java-Thread-Pools und der Executor-Prinzipien im Detail erläutern, um jedem zu helfen, diesen Teil des Wissens zu verstehen

Detaillierte Analyse der Java-Thread-Pool- und Executor-Prinzipien

Thread-Pool-Funktion und Grundwissen

Bevor wir beginnen, besprechen wir „ Threads“ Das Konzept des „Pools“. „Thread-Pool“ ist, wie der Name schon sagt, ein Thread-Cache. Es handelt sich um eine Sammlung von einem oder mehreren Threads. Benutzer können die auszuführenden Aufgaben einfach in den Thread-Pool werfen, ohne sich zu sehr mit den Details der Ausführung zu beschäftigen. Was sind also die Funktionen des Thread-Pools? Oder was sind die Vorteile gegenüber der direkten Verwendung von Thread? Ich habe die folgenden Punkte kurz zusammengefasst:

Reduzieren Sie den durch Thread-Erstellung und -Zerstörung verursachten Verbrauch

Für die Implementierung von Java Thread habe ich im vorherigen Artikel geschrieben In diesem Blog analysiert. Java-Thread und Kernel-Thread sind 1:1 (Linux). Darüber hinaus verfügt Thread über viele Mitgliedsdaten in der Java-Schicht und der C++-Schicht, sodass Java Thread tatsächlich relativ umfangreich ist. Das Erstellen und Zerstören eines Java-Threads erfordert viel Arbeit sowohl vom Betriebssystem als auch von der JVM. Wenn der Java-Thread also zwischengespeichert wird, kann eine gewisse Effizienzverbesserung erzielt werden.

Bequemere und transparentere Implementierung der Computerressourcensteuerung

Um dies zu diskutieren, müssen Sie möglicherweise einige Beispiele nennen. Nehmen Sie als Beispiel den sehr berühmten Webserver Nginx, der für seine leistungsstarken Parallelitätsfähigkeiten und seinen geringen Ressourcenverbrauch bekannt ist. Um diese strengen Anforderungen zu erfüllen, begrenzt Nginx die Anzahl der Arbeitsthreads streng (Arbeitsthreads entsprechen im Allgemeinen der Anzahl der CPUs). Der Schwerpunkt dieses Entwurfs liegt auf der Reduzierung des durch Threadwechsel verursachten Leistungsverlusts. Diese Optimierungsmethode ist auch auf Java anwendbar. Wenn für jede Aufgabe ein neuer Thread erstellt wird, ist das Endergebnis, dass die Programmressourcen schwer zu kontrollieren sind (eine bestimmte Funktion füllt die CPU) und die Gesamtausführungsgeschwindigkeit relativ langsam ist. Der Java-Thread-Pool stellt FixedThreadPool bereit, mit dem Sie die maximale Anzahl von Threads steuern können.

Bei so viel oben erwähntem „Unsinn“ analysieren wir es anhand der Implementierung des Java-Thread-Pools! Der Thread-Pool von Java verfügt über mehrere Implementierungen:

cached ThreadPool

Das Merkmal des zwischengespeicherten Thread-Pools besteht darin, dass er vorherige Threads und neu übermittelte Aufgaben zwischenspeichert. Kann ausgeführt werden zwischengespeicherte Threads, wodurch der erste oben erwähnte Vorteil erreicht wird.

fester ThreadPool

Eine Funktion von CachedThreadPool besteht darin, dass ein neuer Thread erstellt wird, wenn kein inaktiver Thread zum Ausführen der neu übermittelten Aufgabe vorhanden ist. FixedThreadPool tut dies nicht. Es speichert die Aufgabe und wartet, bis ein inaktiver Thread vorhanden ist, bevor sie ausgeführt wird. Das heißt, der zweite oben erwähnte Vorteil wird erreicht.

geplanter ThreadPool

Das Merkmal des geplanten ThreadPools besteht darin, dass er eine Aufgabenplanung realisieren kann, z. B. die verzögerte Ausführung und die periodische Ausführung von Aufgaben.

Zusätzlich zu den oben genannten drei implementiert Java auch newWorkStealingPool, das auf dem Fork/Join-Framework basiert. Ich habe mich noch nicht damit befasst, also lasse ich es lieber. Bei der Parallelitätsunterstützung von Java wird Executor zum Packen verschiedener Thread-Pools verwendet. Der Name „Executor“ ist eigentlich nur ein Executor.

1. Implementierung von zwischengespeichertem ThreadPool und festem ThreadPool

Wie aus der vorherigen Beschreibung ersichtlich ist, sind diese beiden Thread-Pools sehr ähnlich. Dies ist tatsächlich der Fall. Wenn nicht, schauen wir uns ein praktisches Beispiel an:


ThreadPoolExecutor executor1 = (ThreadPoolExecutor)Executors.newCachedThreadPool();
Nach dem Login kopieren


ThreadPoolExecutor executor2 = (ThreadPoolExecutor)Executors.newFixedThreadPool(4);
Nach dem Login kopieren

Dies sind zwei Methoden zum Erstellen neuer Thread-Pools. Sie sehen sehr ähnlich aus! Wenn Sie das nicht glauben, kann ich Ihnen nur die Wahrheit zeigen.


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>());
}
Nach dem Login kopieren

Ja, sie rufen denselben Konstruktor auf, nur mit leicht unterschiedlichen Parametern. Werfen wir also einen Blick auf die Bedeutung dieser Parameter und den Unterschied zwischen den beiden Parametersätzen. Zunächst müssen Sie noch den Konstruktor von ThreadPoolExecutor veröffentlichen.


public ThreadPoolExecutor(int corePoolSize,
             int maximumPoolSize,
             long keepAliveTime,
             TimeUnit unit,
             BlockingQueue<Runnable> workQueue) {
  this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
     Executors.defaultThreadFactory(), defaultHandler);
}
Nach dem Login kopieren

Um frisch auszusehen, werde ich den Konstruktor einer anderen Ebene nicht veröffentlichen, und dieser Konstruktor ist nur eine einfache Zuweisung. Der Funktionsprototyp hier kann uns bereits viele Informationen geben. Ich muss sagen, dass die Benennung des JDK-Codes wirklich gut ist, genau wie Kommentare.

maximumPoolSize ist die maximale Anzahl von Threads im Thread-Pool. Dieser Wert ist Integer.MAX_VALUE, was im Grunde einer Unendlichkeit entspricht. Welche Art von Maschine kann Milliarden von Threads ausführen? ! Bei einem festen ThreadPool ist dieser Wert die vom Benutzer festgelegte Anzahl von Thread-Pools.
keepAliveTime und Unit bestimmen die Cache-Ablaufzeit des Threads; für den zwischengespeicherten ThreadPool beträgt die Cache-Ablaufzeit des Threads eine Minute. Mit anderen Worten, wenn ein Arbeitsthread eine Minute lang nichts zu tun hat, wird er widerrufen um Geld zu sparen. Die an den festen ThreadPool übergebene Zeit beträgt 0, was bedeutet, dass der Arbeitsthread im festen ThreadPool niemals abläuft.

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());
}
}
Nach dem Login kopieren

首先,来看看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);
}
Nach dem Login kopieren

到这里,看起来线程池实现的整体思路其实也没多么复杂。但是还有一个问题——一个普通的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);
  }
}
Nach dem Login kopieren

关键就在这个while的判断条件,对于需要cached线程的情况下,getTask()会阻塞起来,如果缓存的时间过期,就会返回一个null,然后Worker就退出了,也就结束了它的服役周期。而在有任务的情况下,Woker会把task拿出来,然后调用task.run()执行任务,并通过Future通知客户线程(即future.get()返回)。这样一个简单的线程池使用过程就完了。。。

当然,线程池的很多精髓知识——基于线程安全的设计,我都没有分析。有兴趣可以自己分析一下,也可以和我讨论。此外Scheduled ThreadPool这里也没有分析,它的要点其实是调度,主要是根据时间最小堆来驱动的。

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Prinzipien von Thread-Pool und Executor in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
2 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Repo: Wie man Teamkollegen wiederbelebt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Abenteuer: Wie man riesige Samen bekommt
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Quadratwurzel in Java Quadratwurzel in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Quadratwurzel in Java. Hier diskutieren wir anhand eines Beispiels und seiner Code-Implementierung, wie Quadratwurzel in Java funktioniert.

Perfekte Zahl in Java Perfekte Zahl in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur perfekten Zahl in Java. Hier besprechen wir die Definition, Wie prüft man die perfekte Zahl in Java?, Beispiele mit Code-Implementierung.

Zufallszahlengenerator in Java Zufallszahlengenerator in Java Aug 30, 2024 pm 04:27 PM

Leitfaden zum Zufallszahlengenerator in Java. Hier besprechen wir Funktionen in Java anhand von Beispielen und zwei verschiedene Generatoren anhand ihrer Beispiele.

Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

Leitfaden für Weka in Java. Hier besprechen wir die Einführung, die Verwendung von Weka Java, die Art der Plattform und die Vorteile anhand von Beispielen.

Armstrong-Zahl in Java Armstrong-Zahl in Java Aug 30, 2024 pm 04:26 PM

Leitfaden zur Armstrong-Zahl in Java. Hier besprechen wir eine Einführung in die Armstrong-Zahl in Java zusammen mit einem Teil des Codes.

Smith-Nummer in Java Smith-Nummer in Java Aug 30, 2024 pm 04:28 PM

Leitfaden zur Smith-Zahl in Java. Hier besprechen wir die Definition: Wie überprüft man die Smith-Nummer in Java? Beispiel mit Code-Implementierung.

Fragen zum Java Spring-Interview Fragen zum Java Spring-Interview Aug 30, 2024 pm 04:29 PM

In diesem Artikel haben wir die am häufigsten gestellten Fragen zu Java Spring-Interviews mit ihren detaillierten Antworten zusammengestellt. Damit Sie das Interview knacken können.

Brechen oder aus Java 8 Stream foreach zurückkehren? Brechen oder aus Java 8 Stream foreach zurückkehren? Feb 07, 2025 pm 12:09 PM

Java 8 führt die Stream -API ein und bietet eine leistungsstarke und ausdrucksstarke Möglichkeit, Datensammlungen zu verarbeiten. Eine häufige Frage bei der Verwendung von Stream lautet jedoch: Wie kann man von einem Foreach -Betrieb brechen oder zurückkehren? Herkömmliche Schleifen ermöglichen eine frühzeitige Unterbrechung oder Rückkehr, aber die Stream's foreach -Methode unterstützt diese Methode nicht direkt. In diesem Artikel werden die Gründe erläutert und alternative Methoden zur Implementierung vorzeitiger Beendigung in Strahlverarbeitungssystemen erforscht. Weitere Lektüre: Java Stream API -Verbesserungen Stream foreach verstehen Die Foreach -Methode ist ein Terminalbetrieb, der einen Vorgang für jedes Element im Stream ausführt. Seine Designabsicht ist

See all articles