Frage

Kannst du den Fehler erkennen? Dies wird eine werfen java.lang.OutOfMemoryError.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestTheads {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        while(true) {
            executorService.submit(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
    }

}

Der Fehler ist, dass ich anrufe executorService.submit() Anstatt von executorService.execute(), Weil submit() Gibt eine zurück Future Objekt, das ich ignoriere. Mit execute(), Dieses Programm wird tatsächlich für immer laufen.

Man hat jedoch nicht immer den Luxus, eine zu haben execute() Methode, wie bei Verwendung a ScheduledExecutorService:

public static void main(String[] args) {
    // this will FAIL because I ignore the ScheduledFuture object
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
    while(true) {
        executorService.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
}

Was soll man mit Aufgaben tun, die nichts zurückgeben, sondern nur berechnen?

Alle Ideen wären dankbar geschätzt!

BEARBEITEN: ThreadPoolExecutors purge() Sah vielversprechend, aber es säuberte nur stornierte Aufgaben.

War es hilfreich?

Lösung

Das Future Objekt, das zurückgegeben wird ExecutorService Nur bis es ausgeführt wird. (Es ist eigentlich ein FutureTask Instanz, die zu Ihrem delegiert Runnable.) Sobald es ausgeführt wurde, wird es Müll gesammelt, da der Anrufer keinen Bezug auf ihn hat. Mit anderen Worten, das Gedächtnisproblem hat nichts mit der Behandlung der Future.

Wenn Ihnen der Speicher ausgeht, liegt dies daran, dass die Arbeitswarteschlange Millionen von Aufgaben in der Warteschlange hat. Wie in jeder Warteschlange wird sich die Warteschlange nicht ausfüllen, sofern die durchschnittliche Verbrauchsrate nicht die durchschnittliche Produktionsrate überschreitet. Der Inhalt der Warteschlange verbraucht Speicher.

Verwenden Sie eine begrenzte Warteschlange, die effektiv, die Taskwarteschlange droselt oder mehr Speicher erhält.

Dieser Code wird "für immer" ausgeführt:

  ExecutorService executorService = Executors.newFixedThreadPool(1);
  while(true) {
    executorService.submit(new Runnable() {
      public void run() {
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) { }
      }
    });
    Thread.sleep(12);
  }

Der Unterschied liegt nicht in der Behandlung der resultierenden Future Instanzen, aber diese Aufgaben werden mit einer Rate in der Warteschlange gestellt, mit der sie verarbeitet werden können.

Andere Tipps

Es sind nicht so sehr die Zukunft, die zurückgegeben werden, das Ihr Problem ist. Das Problem ist, dass für jeden von Ihnen eingereichten Runnable der ExecutorService sie speichert, um zu einem späteren Zeitpunkt verarbeiten zu können. Jedes Mal, wenn Sie die Sendemethode mit einem Runnable (oder einer Zukunft) aufrufen, wird der Executorservice diesen Runnable zu einer Arbeiterwarteschlange übertragen. Der Runnable sitzt dort, bis ein Thread diesen Runnable von der Warteschlange (die spätere Zeit) auswählen kann. Wenn alle Arbeiter -Threads besetzt sind, wird der Testamentsvollstrecker einfach die Runnable in die Warteschlange bringen.

Ihr Problem ist also, dass Sie nur einen Thread haben, der versucht, eine Warteschlange zu ziehen, die von einem anderen Thread unendlich hinzugefügt wird. Es wird viel schneller hinzugefügt als der Arbeiter -Thread kann jeden Lauf verarbeiten.

Bearbeiten: Das Codebeispiel, das ich als NOS angab, der sich entzogen hat, wirft tatsächlich eine AblehnungsexecutionException aus, sodass der Mechanismus zum Droseln etwas anders sein müsste, wenn Sie wählen würden.

Was eine bessere Lösung angeht, wie ich in meinem Kommentar erwähnt habe; Wenn Sie erwarten, den Testamentsvollstrecker so zu füllen, dass die Arbeiter -Threads nicht mit der Warteschlange Schritt halten können, können Sie Anfragen serialisieren und deserialisieren, wenn sie hereinkommen (erstellen Sie Ihr eigenes Threadpoolexecutor), aber ich würde sicherstellen, dass die Notwendigkeit eines solchen erforderlich ist Fall ist absolut notwendig.

Denken Sie daran, nachdem die Arbeit erledigt ist, dass diese Zukunft verschwunden sind und der Müll gesammelt wird. Wenn Sie also eine Zukunft pro Sekunde ausführen und es unter einer Sekunde ausgeführt wird, wird die Zukunft selbst entfernt und Sie werden kein Speicherproblem haben. Aber wenn Sie eine Zukunft eine Sekunde machen und die Threads alle 3 Sekunden eine Zukunft machen, wird das anziehen und ausgeben.

Bearbeiten: Ich habe den Haufen des von Ihnen ausgeführten Programms vorgestellt und das Problem ist genau das. Das Futuretask, das vom Executorservice erstellt wird, sitzt in der Arbeiterwarteschlange

Class Name                                                       | Shallow Heap | Retained Heap | Percentage 
------------------------------------------------------------------------------------------------------------
java.util.concurrent.ThreadPoolExecutor @ 0x78513c5a0            |          104 | 2,051,298,872 |     99.99% 
|- java.util.concurrent.LinkedBlockingQueue @ 0x785140598        |           80 | 2,051,298,216 |     99.99% 
|  |- java.util.concurrent.LinkedBlockingQueue$Node @ 0x785142dd8|           32 | 2,051,297,696 |     99.99% 
------------------------------------------------------------------------------------------------------------

Die Heap -Analyse geht ein wenig ab, es gibt viele Linked -Blocking -$ -Kode -Analyse, wie Sie es sich vorstellen können

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top