toegang tot variabelen en onderdelen zwenken via verschillende threads

Deze vraag is enigszins gerelateerd aan degene die ik vroeg HIER . Nu heb ik een klasse "Controller" die bestaat uit de hoofdmethode en alle swingcomponenten. er is een klasse genaamd "VTOL" die bestaat uit een variabele genaamd "hoogte" (ik heb deze variabele vanaf nu als vluchtig verklaard).

hier is een klasse die bestaat uit een thread die op de achtergrond draait:

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Vineet
 */
public class Gravity extends Thread {

    String altStr;
    double alt;
    Controller ctrl = new Controller();

    @Override
    public void run() {
        while (true) {
            alt=VTOL.altitude;
            System.out.println(alt);
            alt = alt-0.01;
            VTOL.altitude= (int) alt;
            altStr=new Integer(VTOL.altitude).toString();
            ctrl.lblAltitude.setText(altStr);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
               //TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
}

Ten eerste was het probleem waarmee ik aanvankelijk geconfronteerd werd, dat ik de waarde van "hoogte" niet bij kon werken, het bleef 0 tijdens de uitvoering van het programma. Dus verklaarde ik het als vluchtig (ik weet niet of het een goede gewoonte is)

Ten tweede is er een jLabel in Controller-klasse met de naam "lblAltitude", ik wil de waarde bijwerken zoals deze in deze thread is gewijzigd, maar op de een of andere manier gebeurt dat niet. Hoe kan ik dat doen?

4
U verandert helemaal VTOL.altitude niet. Je kopieert het in een double , trekt 0.01 af en gooit dat terug naar een int voordat je het opnieuw kopieert. Dit verandert niet < code> VTOL.altitude omdat de cast het breukgedeelte zal verwijderen, inclusief de -0.01 . Is VTOL.altitude een verdubbeling? Zo niet, dan wil je het misschien zo maken. Je moet zeker de cast verwijderen naar int .
toegevoegd de auteur OldCurmudgeon, de bron
In dat geval heeft u iets nodig als alt - = alt * 0.01 of niet? Als je echt afneemt met een factor . BTW - als het begint bij 0 verandert het nog steeds niet, zelfs niet met deze bewerking.
toegevoegd de auteur OldCurmudgeon, de bron
eigenlijk is VTOL.altitude een int. mijn doel is om vtol te verlagen met factor 0,01. ik wil niet dat het helemaal naar beneden komt. dus ik deed het.
toegevoegd de auteur md1hunox, de bron

3 antwoord

Een oplossing is om een ​​SwingPropertyChangeSupport-object te gebruiken, om van hoogte een "gebonden" eigenschap te maken met dit ondersteuningsobject, om uw GUI-luisteraar naar deze modelklasse te laten verwijzen en om de GUI van hoogtewijzigingen op de hoogte te stellen.

b.v.

import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;

public class Gravity implements Runnable {
   public static final String ALTITUDE = "altitude";
   private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
   private volatile double altitude;

   @Override
   public void run() {
      while (true) {
         double temp = altitude + 10;
         setAltitude(temp);//fires the listeners
         try {
            Thread.sleep(10);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

   }

   public double getAltitude() {
      return altitude;
   }

   public void setAltitude(double altitude) {
      Double oldValue = this.altitude;
      Double newValue = altitude;

      this.altitude = newValue;

     //this will be fired on the EDT since it is a SwingPropertyChangeSupport object
      swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      swingPcSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      swingPcSupport.removePropertyChangeListener(listener);
   }


}

Voor een vollediger uitvoerbaar voorbeeld:

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;

public class GravityTestGui extends JPanel {
   private static final long ALT_SLEEP_TIME = 400;
   private static final double ALT_DELTA = 5;
   JLabel altitudeLabel = new JLabel("     ");
   private Gravity gravity = new Gravity(ALT_SLEEP_TIME, ALT_DELTA);

   public GravityTestGui() {
      add(new JLabel("Altitude:"));
      add(altitudeLabel);

      gravity.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (Gravity.ALTITUDE.equals(pcEvt.getPropertyName())) {
               String altText = String.valueOf(gravity.getAltitude());
               altitudeLabel.setText(altText);
            }
         }
      });

      new Thread(gravity).start();
   }

   private static void createAndShowGui() {
      GravityTestGui mainPanel = new GravityTestGui();

      JFrame frame = new JFrame("GravityTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }


}

class Gravity implements Runnable {
   public static final String ALTITUDE = "altitude";
   private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this);
   private volatile double altitude;
   private long sleepTime;
   private double delta;

   public Gravity(long sleepTime, double delta) {
      this.sleepTime = sleepTime;
      this.delta = delta;
   }

   @Override
   public void run() {
      while (true) {
         double temp = altitude + delta;
         setAltitude(temp);//fires the listeners
         try {
            Thread.sleep(sleepTime);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

   }

   public double getAltitude() {
      return altitude;
   }

   public void setAltitude(double altitude) {
      Double oldValue = this.altitude;
      Double newValue = altitude;

      this.altitude = newValue;

     //this will be fired on the EDT since it is a SwingPropertyChangeSupport object
      swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      swingPcSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      swingPcSupport.removePropertyChangeListener(listener);
   }
}
7
toegevoegd
@vineetrok Ik moet corrigeren dat tot Java7 er slechts één EDT is, Pete ---> blij dat ik kon om je code +1 uit te voeren, dank je
toegevoegd de auteur mKorbel, de bron
@ user1329572: en ik kende die beperking niet, bedankt voor de informatie. Als u 1.5 moet gebruiken, is het eenvoudig om de melding van de ondersteuning op de EDT in de wachtrij te zetten.
toegevoegd de auteur Hovercraft Full Of Eels, de bron
+1, ik was me helemaal niet bewust van de klasse SwingPropertyChangeSupport . Wat een vondst! Maar het is belangrijk op te merken dat deze klasse alleen beschikbaar is in Java versie 1.6 of hoger.
toegevoegd de auteur user1329572, de bron
@HovercraftFullOfEels: Bedankt! kreeg wat ik zocht. heb iets nieuws geleerd.
toegevoegd de auteur md1hunox, de bron

Telkens wanneer u een Swing-component wijzigt, moet u ervoor zorgen dat deze gebeurtenis plaatsvindt in de draad voor het verzenden van gebeurtenissen (dat wil zeggen EDT).

6
toegevoegd
1. Er is een link naar de regels van gelijktijdigheid met Swing op elke Swing component javadoc, maar de helft van de Swing-vragen hier respecteren de regels niet. Zucht. docs.oracle.com/javase/ 6/docs/api/javax/swing/& hellip;
toegevoegd de auteur JB Nizet, de bron
@vineetrok: nee. er is maar één gebeurtenisthread, dat is een soort van het hele punt ervan. 1+ tot user1329 ...
toegevoegd de auteur Hovercraft Full Of Eels, de bron
is er meerdere EDT mogelijk? is het de juiste manier om het te doen? kortom kan ik het in de bovenstaande klas doen?
toegevoegd de auteur md1hunox, de bron
@ user1329572: Heel erg bedankt!
toegevoegd de auteur md1hunox, de bron

Een derde benadering zou zijn om uw Swing-component op de hoogte te stellen van het model, VTOL.

In Gravity zou je VTOL.altitude updaten en vervolgens opnieuw schilderen op het onderdeel. bijv.

while (true) {
  VTOL.altitude -= 0.01;
  VTOL.makeAnyOtherChangesHereAsWell();

  controller.repaint();
 //sleep, break etc. left as an exercise for the reader
}

Vervolgens, in de methode paintComponent() (of misschien ergens anders in alle paint-aanroepen, is er een kleine kans dat het ergens anders moet zijn ...) van Controller, waarvan u weet dat op de EDT wordt uitgevoerd

// update my widgets from the VTOL model - may want this in a method
String altStr=new Integer(VTOL.altitude).toString();
this.lblAltitude.setText(altStr);  
// may be more, e.g. ...
this.lblFuelSupply.setText(VTOL.getFuelSupply());

super.paintComponent(); //now go draw stuff...

Dit is een beetje strakker gekoppeld dan SwingPropertyChangeSupport, maar de koppeling is alles tussen zeer gerelateerde klassen, dus het is "redelijk", en in sommige opzichten kan dit "duidelijker" zijn. En de Event Dispatch Queue combineert meerdere repaints, dus dit is niet zo inefficiënt als het eerst verschijnt. Als meerdere threads dingen bijwerken en meerdere repaints in de wachtrij plaatsen (), doet alleen de laatste repaint() daadwerkelijk iets.

Een nadeel is dat als je GUI een aantal widgets heeft en je ze allemaal bijwerkt elke keer dat dit een beetje traag gaat. Maar processors zijn tegenwoordig ongelofelijk snel.

2
toegevoegd