Heim  >  Artikel  >  Java  >  Eine kurze Einführung in Thread-Pools in der Java-Programmierung

Eine kurze Einführung in Thread-Pools in der Java-Programmierung

高洛峰
高洛峰Original
2017-02-07 15:06:151285Durchsuche

Ab Java 5 stellt Java einen eigenen Thread-Pool zur Verfügung. Ein Thread-Pool ist ein Thread-Container, der jeweils nur eine bestimmte Anzahl von Threads ausführt. java.util.concurrent.ThreadPoolExecutor ist ein solcher Thread-Pool. Es ist sehr flexibel, aber auch kompliziert in der Anwendung. In diesem Artikel wird es vorgestellt.

Der erste ist der Konstruktor. Nehmen Sie als Beispiel den einfachsten Konstruktor:

public ThreadPoolExecutor(
      int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue)

sieht ziemlich kompliziert aus. Hier ist eine Einführung.
corePoolSize bezieht sich auf die reservierte Thread-Poolgröße.
maximumPoolSize bezieht sich auf die maximale Größe des Thread-Pools.
keepAliveTime bezieht sich auf den Timeout-Zeitraum, bis der Leerlauf-Thread endet.
unit ist eine Aufzählung, die die Einheit von keepAliveTime darstellt.
workQueue stellt die Warteschlange dar, in der Aufgaben gespeichert werden.
Wir können die Bedeutung dieser Parameter aus dem Arbeitsprozess des Thread-Pools verstehen. Der Arbeitsprozess des Thread-Pools ist wie folgt:

1. Wenn der Thread-Pool zum ersten Mal erstellt wird, ist kein Thread darin. Die Aufgabenwarteschlange wird als Parameter übergeben. Selbst wenn sich Aufgaben in der Warteschlange befinden, werden diese vom Thread-Pool nicht sofort ausgeführt.

2. Beim Aufrufen der Methode „execute()“ zum Hinzufügen einer Aufgabe trifft der Thread-Pool die folgende Beurteilung:
a. Wenn die Anzahl der laufenden Threads kleiner als corePoolSize ist, wird sofort ein Thread erstellt um die Aufgabe auszuführen;
b. Wenn die Anzahl der ausgeführten Threads größer oder gleich corePoolSize ist, stellen Sie diese Aufgabe in die Warteschlange.
c. Wenn die Warteschlange zu diesem Zeitpunkt voll ist und die Anzahl der ausgeführten Threads geringer ist als die maximale Poolgröße, müssen Sie dennoch einen Thread erstellen, um die Aufgabe auszuführen Wenn die Anzahl der laufenden Threads größer oder gleich MaximumPoolSize ist, löst der Thread-Pool eine Ausnahme aus und teilt dem Aufrufer mit: „Ich kann keine Aufgaben mehr annehmen.“

3. Wenn ein Thread eine Aufgabe abschließt, nimmt er eine Aufgabe aus der Warteschlange zur Ausführung.

4. Wenn ein Thread nichts zu tun hat und eine bestimmte Zeit (keepAliveTime) überschreitet, stellt der Thread-Pool fest, dass der Thread gestoppt wird, wenn die Anzahl der aktuell ausgeführten Threads größer als corePoolSize ist. Nachdem alle Aufgaben des Thread-Pools abgeschlossen sind, wird er schließlich auf die Größe von corePoolSize verkleinert.

Dieser Prozess zeigt, dass es nicht unbedingt bedeutet, dass die Aufgabe zuerst ausgeführt wird, wenn sie zuerst hinzugefügt wird. Angenommen, die Warteschlangengröße beträgt 10, corePoolSize beträgt 3 und MaximumPoolSize beträgt 6. Wenn dann 20 Aufgaben hinzugefügt werden, ist die Ausführungsreihenfolge wie folgt: Zuerst werden die Aufgaben 1, 2 und 3 ausgeführt, dann die Aufgaben 4 bis 13 in die Warteschlange stellen. Zu diesem Zeitpunkt ist die Warteschlange voll, die Aufgaben 14, 15 und 16 werden sofort ausgeführt und die Aufgaben 17 bis 20 lösen eine Ausnahme aus. Die endgültige Reihenfolge ist: 1, 2, 3, 14, 15, 16, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13. Das Folgende ist ein Beispiel für die Verwendung eines Thread-Pools:

public static void main(String[] args) {
  BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
  ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);
   
  for (int i = 0; i < 20; i++) {
    executor.execute(new Runnable() {
   
      public void run() {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(String.format("thread %d finished", this.hashCode()));
      }
    });
  }
  executor.shutdown();
}

Die Beschreibung dieses Beispiels lautet wie folgt:


1 Nur eine Schnittstelle. Zu den häufig verwendeten Implementierungsklassen gehören LinkedBlockingQueue und ArrayBlockingQueue. Der Vorteil der Verwendung von LinkedBlockingQueue besteht darin, dass es keine Größenbeschränkung gibt. Da in diesem Fall die Warteschlange nicht voll ist, löst die Funktion „execute()“ keine Ausnahme aus und die Anzahl der im Thread-Pool ausgeführten Threads überschreitet niemals „corePoolSize“, sodass der Parameter „keepAliveTime“ bedeutungslos ist.

2. Die Shutdown()-Methode blockiert nicht. Nach dem Aufruf der Methode „shutdown()“ wird der Hauptthread sofort beendet und der Thread-Pool wird weiter ausgeführt, bis alle Aufgaben ausgeführt sind. Wenn die Methode „shutdown()“ nicht aufgerufen wird, bleibt der Thread-Pool am Leben, sodass jederzeit neue Aufgaben hinzugefügt werden können.

Bisher haben wir nur einen kleinen Teil dieses Threadpools vorgestellt. ThreadPoolExecutor ist hoch skalierbar, Voraussetzung für die Erweiterung ist jedoch die Kenntnis seiner Arbeitsweise. In späteren Artikeln wird erläutert, wie die ThreadPoolExecutor-Klasse erweitert wird.

ava.util.concurrent.ThreadPoolExecutor-Klasse bietet umfangreiche Erweiterbarkeit. Sie können sein Verhalten anpassen, indem Sie es in eine Unterklasse umwandeln. Wenn ich beispielsweise nach dem Ende jeder Aufgabe eine Nachricht drucken möchte, das Aufgabenobjekt jedoch nicht ändern kann, kann ich Folgendes schreiben:

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    System.out.println("Task finished.");
  }
};
Zusätzlich zur Methode afterExecute gibt es auch die Klasse ThreadPoolExecutor Die Methoden „beforeExecute()“ und „terminated()“ können überschrieben werden, sodass sie vor der Aufgabenausführung bzw. nach dem Stoppen des gesamten Thread-Pools ausgeführt werden.


除了可以添加任务执行前后的动作之外, ThreadPoolExecutor 还允许你自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。 ThreadPoolExecutor 提供 4 个现有的策略,分别是:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。
这里是一个例子:
ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

除此之外,你也可以通过实现 RejectedExecutionHandler 接口来编写自己的策略。下面是一个例子:

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
    new RejectedExecutionHandler() {
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(String.format("Task %d rejected.", r.hashCode()));
      }
    }
);

更多简单介绍Java编程中的线程池相关文章请关注PHP中文网!


Stellungnahme:
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