Was könnte die Ursache für RejectedExecutionException sein?

74

Ich erhalte diese Ausnahme auf meinem Tomcat-Server (+ liferay)

java.util.concurrent.RejectedExecutionException

Meine Klasse ist so:

public class SingleExecutor extends ThreadPoolExecutor {
  public SingleExecutor(){
    super(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
  }

  @Override
  public void execute(Runnable command) {
    if(command instanceof AccessLogInsert){
        AccessLogInsert ali = (AccessLogInsert)command;
        ali.setConn(conn);
        ali.setPs(ps);
    }
    super.execute(command);
  }
}

Ich erhalte diese Ausnahme in der Zeile. super.execute(command); Dieser Fehler kann auftreten, wenn die Warteschlange voll ist, die LinkedBlockingQueueGröße jedoch 2 ^ 31 beträgt, und ich bin sicher, dass nicht so viele Befehle warten.

Am Anfang ist alles stabil, aber nachdem ich einen Krieg neu eingesetzt habe, beginnt er aufzutreten. Diese Klasse ist nicht Teil des Krieges, sondern in einem Glas in Tomcat / Lib.

Haben Sie eine Idee, warum dies passiert ist und wie Sie es beheben können?

jpprade
quelle

Antworten:

84

Von ThreadPoolExecutor JavaDoc (Schwerpunkt Mine)

Neue Aufgaben, die in der Methode eingereicht wurden, execute(java.lang.Runnable)werden abgelehnt, wenn Executorsie heruntergefahren wurden und wenn die Executorendlichen Grenzen sowohl für die maximale Thread- als auch für die Arbeitswarteschlangenkapazität verwendet werden und gesättigt sind. In beiden Fällen ruft die RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)Methode execute die Methode von its auf RejectedExecutionHandler. Es stehen vier vordefinierte Handlerrichtlinien zur Verfügung:

  1. Standardmäßig ThreadPoolExecutor.AbortPolicylöst der Handler RejectedExecutionExceptionbei Ablehnung eine Laufzeit aus.
  2. In ThreadPoolExecutor.CallerRunsPolicyführt der aufgerufene Thread die Aufgabe selbst aus. Dies bietet einen einfachen Mechanismus zur Steuerung der Rückmeldung, der die Übermittlungsrate neuer Aufgaben verlangsamt.
  3. In ThreadPoolExecutor.DiscardPolicywird eine Aufgabe, die nicht ausgeführt werden kann, einfach gelöscht.
  4. In ThreadPoolExecutor.DiscardOldestPolicy, wenn der Vollstrecker nicht heruntergefahren wird, wird die Aufgabe an der Spitze der Warteschlange gelöscht, und dann wird die Ausführung erneut versucht (die wiederum fehlschlagen kann, so dass diese wiederholt werden.)

Es ist möglich, andere Arten von RejectedExecutionHandlerKlassen zu definieren und zu verwenden . Dies erfordert einige Sorgfalt, insbesondere wenn Richtlinien nur unter bestimmten Kapazitäts- oder Warteschlangenrichtlinien funktionieren.

Vermutlich löst das Nachladen des Krieges daher eine Abschaltung des Krieges aus Executor. Versuchen Sie, die relevanten Bibliotheken in den Krieg zu ziehen, damit Tomcat's ClassLoadereine bessere Chance hat, Ihre App korrekt neu zu laden.

OrangeDog
quelle
2
Der letzte Teil der Antwort ist nett.
Ravindra Babu
"Neue Aufgaben, die in der Methode eingereicht wurden, execute(java.lang.Runnable)werden abgelehnt, wenn der Executor heruntergefahren wurde." Dies führte zu einem Fehler in meinem Code, den ich behoben habe, indem ich den Thread nach dem Herunterfahren 500 ms lang in den Ruhezustand versetzt habe (was möglicherweise nicht erforderlich ist) und dann den Scheduler auf nulldie betreffende Methode gesetzt habe, damit beim nächsten Ausführen einer Aufgabe die betreffende Methode ausgeführt werden muss prüft, ob der Scheduler ist null. Wenn dies der Fall ist, wird eine neue erstellt. Somit entfällt die Ablehnung aus Gründen einer Abschaltung.
Agi Hammerthief
2
@AgiHammerthief Schlafen ist nicht notwendig, aber eine ordnungsgemäße Parallelitätskontrolle ist erforderlich. Während Ihre „Lösung“ Abstürze stoppen kann, verbirgt sie nur das, was sich nach einem großen Problem mit dem Besitz von Ressourcen anhört.
OrangeDog
9

Um die hervorragende Antwort von OrangeDog zu ergänzen, ist der Vertrag von a Executortatsächlich so, dass seine executeMethode ausgelöst wird, RejectedExecutionExceptionwenn der Executor gesättigt ist (dh es ist kein Platz in der Warteschlange vorhanden).

Es wäre jedoch nützlich gewesen, wenn es stattdessen blockiert worden wäre und automatisch gewartet hätte, bis in der Warteschlange Platz für die neue Aufgabe ist.

Mit dem folgenden Brauch ist BlockingQueuees möglich, dies zu erreichen:

public final class ThreadPoolQueue extends ArrayBlockingQueue<Runnable> {

    public ThreadPoolQueue(int capacity) {
        super(capacity);
    }

    @Override
    public boolean offer(Runnable e) {
        try {
            put(e);
        } catch (InterruptedException e1) {
            return false;
        }
        return true;
    }

}

Dies implementiert im Wesentlichen den Gegendruckalgorithmus und verlangsamt den Produzenten, wenn der Executor gesättigt ist.

Verwenden Sie es als:

int n = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(0, n, 1, TimeUnit.MINUTES, new ThreadPoolQueue(n));
for (Runnable task : tasks) {
    executor.execute(task); // will never throw, nor will queue more than n tasks
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
Rustyx
quelle
CallerRunsPolicyexistiert bereits, um dies zu erreichen, und bietet einen besseren Durchsatz als nur das Blockieren.
OrangeDog
Nein, nicht, weil das Blockieren des Produzenten durch Ausführen einer Aufgabe eine Pessimierung ist. Es wird den Pool verhungern lassen, während der Produzent mit dieser einzelnen Aufgabe beschäftigt ist.
Rustyx
Bitte beachten Sie, dass Sie durch einfaches Fangen InterruptedExceptionden Thread nicht unterbrechen. Dies ist selten wünschenswert. Sie sollten generell immer erneut unterbrechen, Thread.currentThread().interrupt();bevor Sie den catchBlock verlassen.
Nilskp