Heim  >  Artikel  >  Java  >  So verwenden Sie den Java-Thread-Pool-Executor

So verwenden Sie den Java-Thread-Pool-Executor

王林
王林nach vorne
2023-04-28 10:01:061840Durchsuche

    Thread-Pool-Klassendiagramm

    So verwenden Sie den Java-Thread-Pool-Executor

    Die am häufigsten verwendete Executors-Implementierung zum Erstellen eines Thread-Pools und zur Verwendung von Threads verwendet hauptsächlich die im obigen Klassendiagramm bereitgestellten Klassen. Das obige Klassendiagramm enthält ein Executor-Framework, ein Framework, das asynchrone Aufgaben basierend auf einer Reihe von Ausführungsstrategieaufrufen plant, ausführt und steuert. Der Zweck besteht darin, einen Mechanismus bereitzustellen, der die Aufgabenübermittlung von der Ausführung der Aufgabe trennt. Es enthält drei Executor-Schnittstellen:

    • Executor: eine einfache Schnittstelle zum Ausführen neuer Aufgaben

    • ExecutorService: erweitert Executor und fügt Methoden zur Verwaltung des Executor-Lebenszyklus und des Aufgabenlebenszyklus hinzu

    • ScheduleExcutorService: Erweiterter ExecutorService zur Unterstützung von Future und regelmäßige Ausführung von Aufgaben

    Vorteile des Thread-Pools

    • Ressourcenverbrauch reduzieren - Vorhandene Threads wiederverwenden, die Kosten für Objekterstellung und -tod reduzieren und die Leistung verbessern

    • Reaktionsgeschwindigkeit - Das ist möglich Kontrollieren Sie effektiv die maximale Anzahl gleichzeitiger Threads, verbessern Sie die Auslastung der Systemressourcen und vermeiden Sie übermäßige Ressourcenkonkurrenz und Blockierung. Wenn eine Aufgabe eintrifft, kann sie sofort ausgeführt werden, ohne auf die Erstellung des Threads warten zu müssen.

    • Nachteile neuer Threads

    • Jedes Mal, wenn ein neuer Thread ein neues Objekt erstellt, ist die Leistung schlecht zu viele Systemressourcen verursachen und einen Absturz oder OOM (Out of Memory Memory Overflow) verursachen, ist die Ursache dieses Problems nicht einfach ein neuer Thread, sondern kann durch Programmfehler oder Designfehler verursacht werden, die zu ständig neuen Threads führen.

      Es fehlen weitere Funktionen wie mehr Ausführungen, regelmäßige Ausführungen, Thread-Unterbrechungen.
    • Thread-Pool-Kernklasse-ThreadPoolExecutor
    • Parameterbeschreibung: ThreadPoolExecutor verfügt über insgesamt sieben Parameter, die zusammen die leistungsstarke Funktion des Thread-Pools bilden.

    • corePoolSize

      : die Anzahl der Kern-Threads

    maximumPoolSize

    : die maximale Anzahl von Threads

    workQueue

    : Blockierungswarteschlange, speichert Aufgaben, die auf ihre Ausführung warten, sehr wichtig, es wird einen erheblichen Einfluss auf den Thread haben Pool läuft Prozess Wenn wir eine neue Aufgabe an den Thread-Pool senden, entscheidet der Thread-Pool basierend auf der Anzahl der aktuell im Pool ausgeführten Threads, wie die Aufgabe behandelt wird. Es gibt drei Verarbeitungsmethoden:

    1. Direktes Umschalten (SynchronusQueue) 2. Unbegrenzte Warteschlange (LinkedBlockingQueue) Die maximale Anzahl an Threads, die erstellt werden können, ist corePoolSize, und MaximumPoolSize funktioniert derzeit nicht. Wenn alle Kernthreads im Thread-Pool ausgeführt werden, werden neue Aufgabenübermittlungen in die Warteschlange gestellt.

    3. Die maximale Poolgröße der begrenzten Warteschlange (ArrayBlockingQueue) kann den Ressourcenverbrauch reduzieren, diese Methode erschwert jedoch die Planung des Thread-Pools. Weil der Thread-Pool und die Warteschlangenkapazität begrenzt sind. Wenn wir also möchten, dass die Durchsatzrate des Thread-Pools und der Verarbeitungsaufgaben einen angemessenen Bereich erreicht, und wenn wir unsere Thread-Planung relativ einfach gestalten und den Ressourcenverbrauch so weit wie möglich reduzieren möchten, müssen wir diese beiden Mengenzuteilungstechniken angemessen einschränken : [ Wenn Sie den Ressourcenverbrauch reduzieren möchten, einschließlich der Reduzierung der CPU-Auslastung, des Ressourcenverbrauchs des Betriebssystems, des Kontextwechsel-Overheads usw., können Sie eine größere Warteschlangenkapazität und eine kleinere Thread-Pool-Kapazität festlegen, wodurch der Durchsatz des Thread-Pools verringert wird . Wenn die von uns übermittelten Aufgaben häufig blockieren, können wir die maximale Poolgröße anpassen. Wenn unsere Warteschlangenkapazität klein ist, müssen wir die Thread-Pool-Größe größer einstellen, damit die CPU-Auslastung relativ höher ist. Wenn jedoch die Kapazität des Thread-Pools zu groß eingestellt und die Anzahl der Aufgaben zu stark erhöht wird, nimmt die Parallelität zu, sodass die Planung zwischen Threads ein Problem darstellt, das berücksichtigt werden muss. Dies kann stattdessen den Durchsatz der Verarbeitungsaufgaben verringern. ]

    keepAliveTime

    : Wie lange der Thread dauern kann, wenn keine Aufgabe ausgeführt wird (wenn die Anzahl der Threads im Thread größer als corePoolSize ist, wenn zu diesem Zeitpunkt keine neue Aufgabe übermittelt wird, der Thread außerhalb des Kernthreads). wird nicht sofort zerstört, sondern wartet, bis die Zeitdauer von keepAliveTime überschritten wird dass neu erstellte Threads die gleiche Priorität haben, Handelt es sich um einen Nicht-Daemon-Thread und der Name ist festgelegt)

    rejectHandler

    : Die Richtlinie, wenn die Verarbeitung einer Aufgabe verweigert wird (die Blockierungswarteschlange ist voll) (die Standardrichtlinie von AbortPolicy löst direkt eine Ausnahme aus, CallerRunsPolicy verwendet den Thread, in dem sich der Aufrufer befindet, um die Aufgabe auszuführen, und DiscardOldestPolicy verwirft die Warteschlange) Die oberste Aufgabe und die aktuelle Aufgabe ausführen, DiscardPolicy verwirft die aktuelle Aufgabe direkt)

    So verwenden Sie den Java-Thread-Pool-Executor

    Die Beziehung zwischen corePoolSize, MaximumPoolSize und workQueue: Wenn die Anzahl der laufenden Threads kleiner als corePoolSize ist, wird direkt ein neuer Thread erstellt, um die Aufgabe zu bearbeiten. Auch wenn andere Threads im Thread-Pool inaktiv sind. Wenn die Anzahl der laufenden Threads größer als corePoolSize und kleiner als MaximumPoolSize ist, wird ein neuer Thread erstellt, um die Aufgabe nur dann zu verarbeiten, wenn die workQueue voll ist. Wenn corePoolSize und maximumPoolSize gleich sind, ist die Größe des erstellten Thread-Pools festgelegt. Zu diesem Zeitpunkt wird eine neue Aufgabe übermittelt. Wenn die WorkQueue nicht voll ist, wird die Anforderung in die WorkQueue gestellt. Warten Sie, bis der leere Thread die Aufgabe aus der workQueue entfernt. Wenn die workQueue zu diesem Zeitpunkt ebenfalls voll ist, verwenden Sie zusätzliche Ablehnungsrichtlinienparameter, um die Ablehnungsrichtlinie auszuführen.

    Initialisierungsmethode: bestehend aus sieben Parametern in vier Initialisierungsmethoden

    So verwenden Sie den Java-Thread-Pool-Executor

    Andere Methoden:

    execute();	//提交任务,交给线程池执行	
    submit();//提交任务,能够返回执行结果 execute+Future
    shutdown();//关闭线程池,等待任务都执行完
    shutdownNow();//关闭线程池,不等待任务执行完
    getTaskCount();//线程池已执行和未执行的任务总数
    getCompleteTaskCount();//已完成的任务数量
    getPoolSize();//线程池当前的线程数量
    getActiveCount();//当前线程池中正在执行任务的线程数量
    # 🎜🎜 #Thread-Pool-Lebenszyklus:

    So verwenden Sie den Java-Thread-Pool-Executor

    • running: Kann neu übermittelte Aufgaben annehmen und auch blockierende Warteschlangen verarbeiten. Aufgaben in # ??? stop: Es können weder neue Aufgaben empfangen noch Aufgaben in der Warteschlange verarbeitet werden.

    • Aufräumen: Wenn alle Aufgaben beendet wurden, beträgt die Anzahl der effektiven Threads 0.

    • terminated: final state
    • Executors verwenden, um einen Thread-Pool zu erstellen
    • Executors können verwendet werden Erstellen Sie vier Thread-Pools: entsprechend den vier oben genannten Thread-Pool-Initialisierungsmethoden

    • Executors.newCachedThreadPool
    • newCachedThreadPool ist ein Thread-Pool, der bei Bedarf neue Threads erstellt, wenn eine Aufgabe ausgeführt wird eingereicht, corePoolSize ist 0 und es wird kein Kernthread erstellt. SynchronousQueue ist eine Warteschlange, die keine Elemente speichert. Es versteht sich, dass die Warteschlange immer voll ist, sodass schließlich Nicht-Kernthreads erstellt werden, um die Aufgabe auszuführen. Nicht zum Kern gehörende Threads werden recycelt, wenn sie 60 Sekunden lang inaktiv sind. Da Integer.MAX_VALUE sehr groß ist, kann davon ausgegangen werden, dass Threads unendlich oft erstellt werden können, was bei begrenzten Ressourcen leicht zu OOM-Ausnahmen führen kann.

      //创建newCachedThreadPool线程池源码
      public static ExecutorService newCachedThreadPool() {
      		/**
              *corePoolSize: 0,核心线程池的数量为0
      		*maximumPoolSize:  Integer.MAX_VALUE,可以认为最大线程数是无限的
      		*keepAliveTime: 60L
      		*unit: 秒
      		*workQueue: SynchronousQueue
              **/
              return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                            60L, TimeUnit.SECONDS,
                                            new SynchronousQueue<Runnable>());
          }
    • Anwendungsfall:
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}",index);
                }
            });
        }
    }

    Es ist erwähnenswert, dass der Rückgabewert von newCachedThreadPool der Typ ExecutorService ist, der nur grundlegende Thread-Pool-Methoden, jedoch keinen Thread-Monitor enthält Berücksichtigen Sie daher die spezifische Situation beim Erstellen eines neuen Threads mithilfe eines Thread-Pooltyps mit dem Rückgabewert ExecutorService.

    Executors.newSingleThreadExecutor

    newSingleThreadExecutor ist ein Single-Threaded-Thread-Pool mit nur einem Kern-Thread und wird mit dem einzigen ausgeführt Gemeinsame Thread-Aufgaben, um sicherzustellen, dass alle Aufgaben in der angegebenen Reihenfolge ausgeführt werden (FIFO, Priorität ...)

    //newSingleThreadExecutor创建线程池源码
    public static ExecutorService newSingleThreadExecutor() {
        /**
          *  corePoolSize : 1,核心线程池的数量为1
    
          *  maximumPoolSize : 1,只可以创建一个非核心线程
    
          *  keepAliveTime : 0L
    
          *  unit => 秒
    
          *  workQueue => LinkedBlockingQueue
          **/
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }

    Wenn eine Aufgabe übermittelt wird, wird zunächst ein Kernthread erstellt, um die Aufgabe auszuführen Wenn die Anzahl der Kernthreads überschritten wird, wird sie in die Warteschlange gestellt, da LinkedBlockingQueue eine Warteschlange mit einer Länge von Integer.MAX_VALUE ist, die als unbegrenzte Warteschlange betrachtet werden kann, sodass unendlich viele Aufgaben in die Warteschlange eingefügt werden können Warteschlange, die bei begrenzten Ressourcen leicht zu OOM-Ausnahmen führen kann. Gleichzeitig haben die Parameter „maximalPoolSize“ und „keepAliveTime“ keine Auswirkung und es werden überhaupt keine Nicht-Kern-Threads erstellt.

    Executors.newFixedThreadPool

    So verwenden Sie den Java-Thread-Pool-ExecutorEin Thread-Pool mit fester Länge, die Anzahl der Kernthreads und die maximale Anzahl von Threads werden vom Benutzer übergeben. Sie können die maximale Anzahl gleichzeitiger Threads festlegen Anzahl der Threads. Wenn die Anzahl überschritten wird, wird in der Warteschlange gewartet Wenn die Ressourcen begrenzt sind, kann es leicht zu OOM-Ausnahmen kommen.

    Executors.newScheduledThreadPool

    Thread-Pool fester Länge, die Anzahl der Kern-Threads wird vom Benutzer übergeben, unterstützt geplante und periodische Aufgabenausführung

    //newFixedThreadPool创建线程池源码
    public static ExecutorService newFixedThreadPool(int nThreads) {
        	/**
              *  corePoolSize : 核心线程的数量为自定义输入nThreads
    
              *  maximumPoolSize : 最大线程的数量为自定义输入nThreads
    
              *  keepAliveTime : 0L
    
              *  unit : 秒
    
              *  workQueue : LinkedBlockingQueue
              **/
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    #🎜🎜 #Wenn eine Aufgabe eingereicht wird, ist corePoolSize eine benutzerdefinierte Eingabe. Nachdem der Kernthread voll ist, werden schließlich Nicht-Kernthreads erstellt, um Aufgaben auszuführen. Nicht zum Kern gehörende Fäden werden nach Gebrauch recycelt. Da Integer.MAX_VALUE sehr groß ist, kann davon ausgegangen werden, dass Threads unendlich oft erstellt werden können, was bei begrenzten Ressourcen leicht zu OOM-Ausnahmen führen kann. Denn die verwendete DelayedWorkQueue kann geplante und periodische Aufgaben umsetzen. ScheduledExecutorService bietet drei Methoden, die verwendet werden können:

    schedule: Führen Sie die Aufgabe nach Verzögerung aus. ScheduleAtFixedRate: Führen Sie die Aufgabe mit der angegebenen Rate aus. ScheduleWithFixedDelay: Mit ausführen angegebene Verzögerung Aufgabenanwendungsfall:

    //newScheduledThreadPool创建线程池源码
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        /**
          *  corePoolSize : 核心线程的数量为自定义输入corePoolSize
    
          *  maximumPoolSize : 最大线程的数量为Integer.MAX_VALUE
    
          *  keepAliveTime : 0L
    
          *  unit : 纳秒
    
          *  workQueue : DelayedWorkQueue
          **/
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    Summary

    FixedThreadPool und SingleThreadExecutor Die zulässige Länge der Anforderungswarteschlange ist Integer.MAX_VALUE, wodurch sich ein großer Wert ansammeln kann Anzahl der Anfragen, was zu einer OOM-Ausnahme führt

  • Die Anzahl der Threads, die von CachedThreadPool und newScheduledThreadPool erstellt werden dürfen, ist Integer.MAX_VALUE, wodurch eine große Anzahl von Threads erstellt werden kann, was zu OOM-Ausnahmen führt

  • Aus diesem Grund ist es verboten, Executors zum Erstellen von Thread-Pools zu verwenden. Es wird jedoch empfohlen, ThreadPoolExecutor selbst zu erstellen. Grund

    So definieren Sie Thread-Pool-Parameter

    CPU-intensiv: Es wird empfohlen, dass die Größe des Thread-Pools die Anzahl der CPUs + 1 beträgt gemäß der Runtime.availableProcessors-Methode IO-intensiv: Anzahl der CPUs * CPU-Auslastung * (1 + Thread-Wartezeit/Thread-CPU-Zeit) Hybridtyp: Aufgaben in CPU-intensiv und IO-intensiv aufteilen und dann verwenden Verschiedene Thread-Pools, um sie zu verarbeiten, sodass jeder Thread-Pool sie entsprechend seiner jeweiligen Arbeitslast verarbeiten kann. Blockierungswarteschlange: Es wird empfohlen, eine begrenzte Warteschlange zu verwenden, um eine Ressourcenerschöpfung zu vermeiden: Die Standardeinstellung ist AbortPolicy-Ablehnungsstrategie, die RejectedExecutionException direkt im Programm auslöst [da es eine Zeitausnahme ausführt, kein erzwungener Fang], diese Verarbeitungsmethode ist nicht elegant genug. Für den Umgang mit Ablehnungen werden die folgenden Strategien empfohlen:

    • Fangen Sie die Ausnahme „RejectedExecutionException“ im Programm ab und verarbeiten Sie die Aufgabe in der abgefangenen Ausnahme. Für die Standard-Ablehnungsrichtlinie

    • verwenden Sie die CallerRunsPolicy-Ablehnungsrichtlinie. Diese Richtlinie übergibt die Aufgabe an den Thread, der „execute“ aufruft [normalerweise den Hauptthread]. für einen bestimmten Zeitraum, sodass der Arbeitsthread laufende Aufgaben verarbeiten kann. Zu diesem Zeitpunkt wird der übermittelte Thread in der TCP-Warteschlange gespeichert. Dies wirkt sich auf den Client aus. Um die Ablehnungsstrategie anzupassen, müssen Sie nur die RejectedExecutionHandler-Schnittstelle implementieren.

    • Wenn die Aufgabe nicht besonders wichtig ist, können Sie auch die Ablehnungsrichtlinien DiscardPolicy und DiscardOldestPolicy verwenden, um die Aufgabe zu verwerfen.

    • Wenn Sie die statische Methode von Executors verwenden, um ein ThreadPoolExecutor-Objekt zu erstellen, können Sie Semaphore verwenden um die Ausführung der Aufgabe einzuschränken und OOM-Ausnahmen zu vermeiden

    Das obige ist der detaillierte Inhalt vonSo verwenden Sie den Java-Thread-Pool-Executor. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen