2017-01-25 2 views
2

Ich habe eine Aufzeichnungsklasse, die alle n Millisekunden nach Werten aus einer Datenquelle fragt. Das Ganze soll asynchron laufen und eine Sammlung zurückgeben, die die aufgezeichneten Daten enthält.Eine Zukunft abbrechen und trotzdem den Rückgabewert abrufen

Meine Idee war ein ExecutorService mit einem einzigen Thread und einem Callable, das die Datenabfrage durchführt. Das Callable soll unterbrochen werden, damit der Caller nicht auf Thread.sleep Calls warten muss. He Callable Interrupt ich rufe Future.cancel(true) über die Zukunft wieder von der ExecutorService:

class Recorder { 
    private DataSource source; 
    private ExecutorService executor; 
    private Future<List<MyData>> dataFuture; 

    private class FixedPollRateCallable { 
     private DataSource source; 
     private long pollrate; 

     public FixedPollrateCallable(DataSource source, long pollrate) { 
      this.source = source; 
      this.pollrate = pollrate; 
     } 

     public List<MyData> call() throws Exception { 
      List<MyData> dataList = new ArrayList<>(); 
      while(!Thread.currentThread().isInterrupted()) { 
       dataList.add(source.getData()); 
       try { 
        Thread.sleep(pollrate); 
       } catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
       } 
      } 
      return dataList; 
     } 
    } 

    public Recorder(DataSource source) { 
     this.source = source; 
     this.executor = Executors.newSingleThreadExecutor(); 
    } 

    public void startRecording(long pollrate) { 
     if(dataFuture != null && !dataFuture.isDone()) 
      throw new RecordingException(); 

     dataFuture = executor.submit(new FixedPollRateCallable(source, pollrate); 
    } 

    public void stopRecording() { 
     dataFuture.cancel(true); 
    } 

    public List<MyData> getRecordedData() throws InterruptedException, ExecutionException { 
     return dataFuture.get(); 
    } 
} 

Aber wenn das Ganze wie folgt nennen:

recorder.startRecording(); 
recorder.stopRecording(); 
recorder.getRecordedData(); 

Ich bekomme eine java.util.concurrent.CancellationException auf dem recorder.getRecordedData() Anruf.

Ich denke, dass neben dem Unterbrechen des Threads, cancel(true) setzt auch ein Abbruch-Flag, so dass Anrufe an Future.get() wird immer scheitern.

Gibt es eine Möglichkeit, oder wissen Sie über eine bessere Alternative, die es mir erlaubt, einen Thread zu unterbrechen, Thread.sleep() ausbrechen und immer noch einen Wert zurückgeben können?

Antwort

3

Nun, fragen Sie nach etwas, das vom Konzept her nicht sinnvoll:

Lets Zitat der javadoc für get():

Wirft: CancellationException - wenn die Berechnung

Und für cancel() wurde abgesagt es heißt:

Nach dieser Methode werden nachfolgende Aufrufe von isDone() immer zurück wahr. Nachfolgende Aufrufe von isCancelled() geben immer true zurück, wenn diese Methode true zurückgibt.

Mit anderen Worten: Sie brauchen hier wahrscheinlich eine andere Art von Mechanismus. Zum Beispiel könnten Sie Ihre Runable zu Push die bereits gesammelten Informationen ändern, anstatt es zurückzugeben.

Mit anderen Worten: statt einer Zukunft zu alle Daten zu einem bestimmten Zeitpunkt zu verwenden; Warum schickst du nicht einfach alle Daten direkt bei der Ankunft an eine "sichere Senke"? Dein Ansatz besteht darin, zu pollen; aber offensichtlich: Polling und Interrupting gehen nicht Hand in Hand!

Edit: jetzt Sie Ihren Recorder wie folgt erstellen:

public Recorder(DataSource source) 

so gebaut Sie "Daten" von source zu erhalten. Eine einfache Lösung könnte sein,

public Recorder(DataSource source, List<MyData> sink) 

als auch.

Und anstatt einen lokalen dataList in Ihrem Runable ... zu halten, fügen Sie einfach an den sink an, der dem Recorder zur Verfügung gestellt wird.

Ich benutze das Wort "sicher" bezieht sich einfach auf die Tatsache, dass Sie darauf achten müssen, wenn mehrere Threads beginnen, die gleiche Liste zum Beispiel zu aktualisieren.

+0

Ich habe nie so darüber nachgedacht. Können Sie mir ein paar Hinweise geben, was eine "sichere Senke" ist? Ich bin neu in der gleichzeitigen Programmierung in Java und es scheint, als ob eine selbstgemachte Lösung fast immer aufgrund einer Race-Bedingung oder eines Caching-Fehlers fehlschlägt. –

+1

Siehe meine aktualisierte Antwort. – GhostCat

+0

Ah, ich verstehe. Da die Recorder-Klasse sowieso eine Fassade für die zugrundeliegende Multithread-Verrücktheit ist, kann ich die List- "Senke" einkapseln, so dass es sicher ist, sie zu erhalten, nachdem die poll-callable (oder runnable in diesem Fall) abgebrochen wurde. Vielen Dank! –

Verwandte Themen