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 dortRuntimeException
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?
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
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
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