2015-03-15 10 views
9

Ich muss eine Bibliothek erstellen, in der ich synchrone und asynchrone Methoden haben werde.Wie kann ExecutorService in der Multithreading-Umgebung besser verwendet werden?

  • executeSynchronous() - wartet, bis ich ein Ergebnis habe, gibt das Ergebnis zurück.
  • executeAsynchronous() - gibt sofort eine Zukunft zurück, die bearbeitet werden kann, nachdem andere Dinge getan wurden, falls erforderlich.
  • Core-Logic meiner Bibliothek

    Der Kunde wird unsere Bibliothek nutzen und sie werden es nennen, indem DataKey Builder-Objekt. Wir werden dann eine URL erstellen, indem wir das Objekt DataKey verwenden und einen HTTP-Client-Aufruf an diese URL ausführen, indem wir sie ausführen. Nachdem wir die Antwort als JSON-String erhalten haben, senden wir diesen JSON-String zurück an unseren Kunden DataResponse Objekt. Einige Kunden werden executeSynchronous() anrufen und einige könnten executeAsynchronous() anrufen, deshalb muss ich zwei Methoden separat in meiner Bibliothek bereitstellen.

    Schnittstelle:

    public interface Client { 
    
        // for synchronous 
        public DataResponse executeSynchronous(DataKey key); 
    
        // for asynchronous 
        public Future<DataResponse> executeAsynchronous(DataKey key); 
    } 
    

    Und dann habe ich meine DataClient die die oben Client Schnittstelle implementiert:

    public class DataClient implements Client { 
    
        private RestTemplate restTemplate = new RestTemplate(); 
        // do I need to have all threads as non-daemon or I can have daemon thread for my use case? 
        private ExecutorService executor = Executors.newFixedThreadPool(10); 
    
        // for synchronous call 
        @Override 
        public DataResponse executeSynchronous(DataKey key) { 
         DataResponse dataResponse = null; 
         Future<DataResponse> future = null; 
    
         try { 
          future = executeAsynchronous(key); 
          dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS); 
         } catch (TimeoutException ex) { 
          PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key); 
          dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR); 
          future.cancel(true); // terminating tasks that have timed out 
         } catch (Exception ex) { 
          PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key); 
          dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
         } 
    
         return dataResponse; 
        } 
    
        //for asynchronous call 
        @Override 
        public Future<DataResponse> executeAsynchronous(DataKey key) { 
         Future<DataResponse> future = null; 
    
         try { 
          Task task = new Task(key, restTemplate); 
          future = executor.submit(task); 
         } catch (Exception ex) { 
          PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key); 
         } 
    
         return future; 
        } 
    } 
    

    Einfach Klasse, die die eigentliche Aufgabe durchführen wird:

    public class Task implements Callable<DataResponse> { 
    
        private DataKey key; 
        private RestTemplate restTemplate; 
    
        public Task(DataKey key, RestTemplate restTemplate) { 
         this.key = key; 
         this.restTemplate = restTemplate; 
        } 
    
        @Override 
        public DataResponse call() { 
         DataResponse dataResponse = null; 
         String response = null; 
    
         try { 
          String url = createURL(); 
          response = restTemplate.getForObject(url, String.class); 
    
          // it is a successful response 
          dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS); 
         } catch (RestClientException ex) { 
          PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key); 
          dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR); 
         } catch (Exception ex) { // should I catch RuntimeException or just Exception here? 
          PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key); 
          dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
         } 
    
         return dataResponse; 
        } 
    
        // create a URL by using key object 
        private String createURL() { 
         String url = somecode; 
         return url; 
        } 
    } 
    

    I habe wenig Fragen zu meiner obigen Lösung -

    • Sollte ich Daemon oder Nicht-Daemon-Threads für meinen obigen Anwendungsfall verwenden?
    • Außerdem beende ich die Aufgaben, die abgelaufen sind, so dass es einen meiner begrenzten 10 Threads für eine lange Zeit nicht besetzt. Sieht das so gut aus, wie ich es mache?
    • In meiner call() Methode erhalte ich Ausnahme. Sollte ich dort RuntimeException fangen? Was ist der Unterschied, wenn ich Exception oder RuntimeException erhalte?
    • Als ich anfing, an dieser Lösung zu arbeiten, beendete ich nicht die Aufgaben, die das Zeitlimit überschritten hatten. Ich habe die Zeitüberschreitung dem Client gemeldet, aber die Aufgabe läuft weiterhin im Thread-Pool (möglicherweise besetzt eine meiner begrenzten 10 Threads für eine lange Zeit). Also habe ich einige Online-Forschung, und ich fand, dass ich meine Aufgaben aufheben können diejenigen haben timed out, indem Sie auf Zukunft mit verwerfen, wie unten gezeigt -

      future.cancel(true); 
      

      Aber ich wollte sicher machen, sieht es richtig so, wie ich bin in meiner executeSynchronous Methode zu tun, um die Aufgaben zu beenden, die Timedout haben?

      Da ich cancel() auf der Future aufrufen, die es stoppen wird zu laufen, wenn Aufgaben noch in der Warteschlange ist, so bin ich nicht sicher, was ich mache, ist richtig oder nicht? Was ist der richtige Ansatz dafür?

      Wenn es einen besseren Weg gibt, kann dann jemand dafür ein Beispiel geben?

      Sollten wir immer die Aufgaben beenden, die abgelaufen sind? Wenn wir das nicht tun, was könnte dann der Einfluss sein?

    +1

    Ich habe nur den Anfang der Frage gelesen (Ich bin nicht in der Stimmung für Code-Review, sorry), aber hier ist ein Vorschlag: Fügen Sie eine einzige Ausführen-Methode auf der Client-Oberfläche, die eine Zukunft zurückgibt. Auf diese Weise kann es ein Implementierungsdetail sein, ob Sie es synchron oder asynchron implementieren. – lbalazscs

    +0

    Dies beantwortet Ihre Fragen nicht, aber haben Sie bereits daran gedacht, zwei verschiedene Implementierungen der Client-Schnittstelle (eine synchrone und eine asynchrone Version) bereitzustellen, anstatt jede Methode zweimal zu verwenden? – isnot2bad

    +0

    Ich habe ein Design wie dieses, da es in meiner Firma einige Kunden gibt, die nur die Synchronisierungsmethode verwenden möchten, aber es gibt einige Kunden, die nur an der asynchronen Methode interessiert sind. Deshalb habe ich beide Methoden. Je nach Kundenbedürfnis können sie diese Methode verwenden. Dies ist auch der Fall bei Open-Source-Projekten wie datastax java driver, sie haben sync- und async-Schreibzugriff, es liegt an mir, welchen ich verwenden möchte. – john

    Antwort

    3

    Sollte ich Dämon oder nicht Daemon-Threads für meine oben Anwendungsfall?

    Kommt drauf an. Aber in diesem Fall würde ich Daemon-Threads bevorzugen, weil es bequem ist, Clients zu verwenden, die den Prozess beenden können.

    Sieht das so gut aus, wie ich es mache?

    Nein, tut es nicht. Das Unterbrechen einer IO-Task ist ziemlich schwierig. Versuchen Sie auch in RestTemplate Timeout zu setzen. Die Annullierung der Zukunft erscheint in diesem Fall bedeutungslos.

    Was ist der Unterschied, wenn ich Exception oder RuntimeException erhalte?

    Wenn Sie in try-Blöcken keine Ausnahmen überprüft haben, gibt es keinen Unterschied :) Nur weil in diesem Fall nur RuntimeExceptions möglich sind.

    Und noch eine wichtige Anmerkung: die Implementierung von Synchronisierungsaufrufen als async + Warten ist eine schlechte Idee. Es ist bedeutungslos und verbraucht einen Thread aus dem Thread-Pool pro Anruf. Erstelle einfach die Instanz der Aufgabe und rufe sie im aktuellen Thread auf!

    +0

    Danke für die Hilfe. In Bezug auf meine zweite Frage, können Sie mir helfen zu verstehen, wie kann ich das Timeout in 'RestTemplate' effizient einstellen? Da diese Bibliothek sehr stark belastet wird, muss sie ziemlich schnell reagieren. Was auch immer der Zeitserver nimmt, es sollte nur so viel Zeit in Anspruch nehmen. – john

    +0

    bist du da? – john

    +0

    Werfen Sie einen Blick: http://StackOverflow.com/Questions/13837012/Spring-RestTemplate-Timeout –

    8

    Sollte ich Daemon oder Nicht-Daemon-Threads für meinen obigen Anwendungsfall verwenden?

    Es hängt davon ab, ob diese Threads das Beenden des Programms verhindern sollen. Die JVM wird beendet, wenn der letzte nicht-Daemon-Thread beendet ist.

    Wenn diese Aufgaben jederzeit beendet werden können, wenn die JVM existiert, sollten sie Daemon sein. Wenn Sie möchten, dass die JVM auf sie wartet, machen Sie sie zu einem Nicht-Daemon.

    See: java daemon thread and non-daemon thread

    Auch ich die Aufgaben bin endet, die aus haben zeitlich so abgestimmt, dass es nicht eine meiner begrenzt 10 Threads für eine lange Zeit nicht besetzen. Sieht das so gut aus, wie ich es mache?

    Ja und nein. Sie rufen cancel() auf dem Future ordnungsgemäß an, der es von dem Ausführen hindert, wenn es sich noch in der Warteschlange befindet. Wenn jedoch der Thread bereits die Aufgabe ausgeführt wird, wird der Abbruch nur den Thread unterbrechen. Wahrscheinlich sind die restTemplate Aufrufe nicht unterbrechbar, so dass der Interrupt ignoriert wird. Nur bestimmte Methoden (wie Thread.sleep(...) sind unterbrechbare und InterruptException werfen So future.cancel(true) Aufruf wird die Operation nicht stoppen und den Thread beenden

    Siehe auch:.. Thread not interrupting

    Eine Sache, die Sie tun können, ist ein cancel() Methode setzen auf Task Objekt, das mit Nachdruck die restTemplate geschlossen wird. Sie müssen damit experimentieren. eine weitere Idee ist eine Art von Timeout auf der restTemplate Verbindung oder IO gesetzt, so dass es nicht ewig warten.

    Wenn Sie mit dem Spring arbeiten RestTemplate dann gibt es keine direkte schließen, aber Sie können die zugrunde liegende Verbindung schließen, die ich glaube, die über die SimpleClientHttpRequestFactory sein kann, so müssen Sie disconnect() auf dem zugrunde liegenden HttpURLConnection aufrufen.

    In meiner call() -Methode erhalte ich Ausnahme. Sollte ich dort RuntimeException fangen?

    RuntimeException erweitert Exception, so dass Sie sie bereits fangen werden.

    Was ist der Unterschied, wenn ich Exception oder RuntimeException fangen?

    Fangen Exception Fänge sowohl die überprüft (nicht-Runtime) Ausnahmen und die Runtime-Ausnahmen. Wird nur die RuntimeException abgefangen, werden alle definierten Ausnahmen nicht abgefangen und von der Methode ausgelöst.

    RuntimeException s sind spezielle Ausnahmen, die nicht durch den Code überprüft werden müssen.Zum Beispiel kann jeder Code eine IllegalArgumentException werfen, ohne die Methode als throws IllegalArgumentException zu definieren. Bei geprüften Ausnahmen handelt es sich um einen Compilerfehler, wenn eine geprüfte Ausnahme nicht von der Aufrufermethode abgefangen oder ausgelöst wird. Dies gilt jedoch nicht unter RuntimeException s.

    Hier ist eine gute Antwort auf das Thema:

    +0

    Danke für den detaillierten Vorschlag. Es gibt kein Leistungsproblem, wenn ich Daemon oder Nicht-Daemon-Thread richtig verwende? Ich habe meinen dritten Punkt verstanden, der eine Ausnahme darstellt. Ich bin verwirrt in Bezug auf Timeout eins. Ich hatte den Eindruck, dass, wenn meine Aufgaben zeitlich begrenzt sind, ich diese Aufgabe abbrechen kann? Was ich suche ist, sagen wir, wenn eine meiner Aufgaben zeitlich begrenzt ist, dann bedeutet das, dass ich mich nicht dafür interessiere, also wie kann ich sie abbrechen? – john

    +0

    Kein Leistungsunterschied btw Daemon-Threads und Nicht-Daemon @david. – Gray

    +0

    Lesen Sie mehr über Threadunterbrechung @david. Es ist schwierig, einen Thread ordnungsgemäß abzubrechen. Sie müssen die 'RestTemplate' schließen, die ich in meiner Antwort erwähnt habe. – Gray

    Verwandte Themen