7

Lassen Sie uns sagen, ich habe zwei Threads wie folgt ausgeführt werden:Funktioniert dieses nicht standardmäßige Java-Synchronisationsmuster?

  • Thread A, die Berechnung durchführt, während Pixel eines gemeinsamen Bildes
  • Thema B periodisch liest das Bild und kopiert sie auf den Bildschirm zu aktualisieren

Thread A führt die Arbeit schnell aus, sagen 1 Million Aktualisierungen pro Sekunde, also vermute ich, dass es eine schlechte Idee wäre, auf einem Lock/Mutex/Monitor so oft zu sperren und zu entsperren. Aber wenn es keine Sperre und keine Möglichkeit gibt, eine Vorkommnis-Beziehung von Thread A zu Thread B herzustellen, ist Thread B durch das Java-Speichermodell (JMM-Spezifikation) nicht garantiert, irgendwelche der A-Aktualisierungen des Bildes zu sehen.

So dachte ich, dass die minimale Lösung für Threads A und B ist, um beide regelmäßig auf der gleichen freigegebenen Sperre zu synchronisieren, aber keine Arbeit ausführen, während im synchronisierten Block - das ist, was das Muster nicht-Standard und macht zweifelhaft. Zur Veranschaulichung in Halbechthalb Pseudo-Code:

class ComputationCanvas extends java.awt.Canvas { 

    private Object lock = new Object(); 
    private int[] pixels = new int[1000000]; 

    public ComputationCanvas() { 
     new Thread(this::runThreadA).start(); 
     new Thread(this::runThreadB).start(); 
    } 

    private void runThreadA() { 
     while (true) { 
      for (1000 steps) { 
       update pixels directly 
       without synchornization 
      } 
      synchronized(lock) {} // Blank 
     } 
    } 

    private void runThreadB() { 
     while (true) { 
      Thread.sleep(100); 
      synchronized(lock) {} // Blank 
      this.repaint(); 
     } 
    } 

    @Override 
    public void paint(Graphics g) { 
     g.drawImage(pixels, 0, 0); 
    } 
} 

Does leer Synchronisationsblöcke auf diese Weise das Hinzufügen korrekt den Effekt erreichen von Daten von Thread A übertragen einzufädeln B? Oder gibt es eine andere Lösung, die ich mir nicht vorstellen konnte?

+1

Warum würde ein atomarer Boolescher nicht den Trick machen? Synchronisieren auf nichts verlässt immer noch die Arbeit aus der Synchronisation – efekctive

+3

Warum nicht 'volatile' verwenden? – shmosel

+0

Möglicherweise verwandt: http://StackOverflow.com/Questions/17108541/Happens-before-relationsshifts-with-Volatile-Fields-and-Synchronized-blocks-in-Jav – flakes

Antwort

1

Ja, es funktioniert. Aber es funktioniert furchtbar.

passiert vorher nur, wenn die Freigabe des Schreibers vor dem Erwerb des Lesers geschieht. Ihre Implementierung setzt voraus, dass das, was Sie gerade schreiben, vor dem nachfolgenden Lesen/Aktualisieren von ThreadB abgeschlossen wird. Wenn Sie verursachen, dass Ihre Daten ständig durch Synchronisieren gelöscht werden, führt dies zu Leistungsproblemen, obwohl ich nicht sicher sein kann. Sicher, Sie haben Ihre Synchronisation feinkörniger gemacht, haben Sie sie schon getestet?

Eine bessere Lösung könnte eine Singleton/Transfer-SPSC-Warteschlange (Single Producer/Single Consumer) verwenden, um den aktuellen Snapshot des Schreib-Threads zu speichern und bei jeder Aktualisierung zu verwenden.

int[] data = ... 
Queue<int[]> queue = new ... 

// Thread A 
while (true) { 
    for (1000 iterations or so) { 
     ... 
    } 
    queue.add(data); 
} 

// Thread B 
while (true) { 
    int[] snapshot = queue.take(); 
    this.repaint(); 
} 

Der Vorteil hierbei ist, dass Sie nicht busywait müssen, können Sie nur für die Warteschlange warten den nächsten Schreib zu blockieren oder bis. Sie können Schreibvorgänge überspringen, für die Sie keine Zeit zum Aktualisieren haben. Sie müssen nicht auf den willkürlichen Thread-Scheduler angewiesen sein, um Datenlecks für Sie zu planen.

Denken Sie daran, dass thread-sichere Datenstrukturen zum Übergeben von Daten zwischen Threads hervorragend geeignet sind.

Bearbeiten: oops, vergessen zu sagen, dass je nachdem, wie Ihre Updates gehen, möchten Sie möglicherweise eine Array-Kopie verwenden, um zu verhindern, dass Ihre Daten aus zufälligen Schreibvorgänge, die nicht im Cache gespeichert sind.

+0

'Repaint' nimmt keine Parameter –

+0

@DavidConrad Sie haben Recht, dachte über die Farbe Methode unten. – xTrollxDudex

+0

Man könnte auch die Pixeldaten durch eine AtomicReference übergeben, aber eine Warteschlange ist aus Gründen, die Sie notiert haben, überlegen - keine hektischen Warte- oder Planungsprobleme. Ich habe dieses Muster viele Male mit großem Erfolg benutzt. – user949300

Verwandte Themen