Race Condition

Status
Nicht offen für weitere Antworten.
U

undertaker

Gast
Mit
Code:
Image pic = ImageIO.read(imgds);
lese ich einen ByteArrayInputStream ein.

Nach dem Einlesen wird das Bild auf Applet-Größe skaliert:
Code:
pic = pic.getScaledInstance(getWidth(), getHeight(), pic.SCALE_SMOOTH);

Hier kommt das Problem!

Nur ab und an funktioniert das ganze und das skalierte Bild erscheint im Applet.
Andernfalls hängt sich das Applet einfach auf.

Wenn ich aber nun auf das Skalieren verzichte (was nicht sein darf) erscheint das Bild (in zu großer Form) ohne Probleme.

Vermutlich handelt es sich hierbei um eine RaceCondition.

Wenn jemand dazu helfen kann?

Thanx
undertaker
 

Wildcard

Top Contributor
Was bringt dich auf die Idee das es eine Race Condition sein könnte?
Wie sieht dein Thread System aus?
Synchronisiert du in irgendeiner Form?
Was sagt die Java Konsole im Fehlerfall?
 

HoaX

Top Contributor
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Image.html#getScaledInstance(int,%20int,%20int)

daher?!
 
U

undertaker

Gast
Code:
 public void showPicImpl() throws IOException{ //Zeigt ein Bild an, was showurl als URL enthält.
        
        JProgressBar downProg = new JProgressBar(0, 100);
        downProg.setStringPainted(true);
        downProg.setString("Loading...");
        downProg.setForeground(Color.orange);
        downProg.setBackground(Color.WHITE);
        this.add(downProg);
        this.validate();
//Download - Verbindung aufbauen:         

         URL getURL = new URL(showurl);
         InputStream in;
         OutputStream out;
         URLConnection con = getURL.openConnection();
         
         con.setDoInput(true);
         con.connect();
         in = con.getInputStream();

         long downov = con.getContentLength();      
         ByteArrayOutputStream bout = new ByteArrayOutputStream();

         //Bytes downloaden:
        byte[] buf = new byte[5000];
        int nread;
        int navailable;
        int total = 0; //Menge an Bytes bisher gesendet
        int percentage = 0; //Percent done...
        int oldpercent = 0;
        byte[] filedata = new byte[con.getContentLength()];
      synchronized (in) {
          while((nread = in.read(buf, 0, buf.length)) > -1) {
            bout.write(buf,0,nread);
           total += nread; //Wieviel bereits gesendet?
            percentage = (int)( ( total * 100.0 ) / downov );
            if(oldpercent < percentage){
               System.out.println("Preview %: " + percentage);
               downProg.setString("Loading preview: "+percentage+"%");
               downProg.setValue(percentage);
               this.validate();
            }
                oldpercent = percentage;
         }
       }
 buf = null;
       
        ByteArrayInputStream imgds = new ByteArrayInputStream(bout.toByteArray());
        BufferedImage  img = ImageIO.read(imgds);
        Image pic = img.getScaledInstance(getWidth(), getHeight(), img.SCALE_SMOOTH);
        this.remove(downProg);
        setImage(pic);
        pic.flush();
    }


Leider hängt sich das ganze meistens nach dem Ladevorgang auf.
Wenn ich aber
Code:
Image pic = img.getScaledInstance(getWidth(), getHeight(), img.SCALE_SMOOTH);
weglasse klappt alles.

Ich _muss_ das Bild aber skalieren.

THanx
undertaker
 

Redfrettchen

Bekanntes Mitglied
Hast du versucht, die Image-Erstellung zu synchronisieren oder mit einem ReentrantLock zu schützen? Vielleicht hilft auch SwingUtilities.invokeLater(Runnable), dem du einen Thread übergibst, der setImage(Image) aufruft (wahrscheinlich ist das eher Aberglaube, aber man kann's ja mal versuchen ^^).
Eine unsaubere Lösung wäre, einfach für ein paar Millisekunden eine Pause einzulegen und es danach zu versuchen.
 
U

undertaker

Gast
Genau hier ist das Problem!
Ich erzeuge einen neuen Thread zum Herunterladen eines Bildes (Ladebalken)...

Dieser Thread zeigt dann das Bild an wenn alles fertig heruntergeladen ist.

Während der Download-Thread das Bild herunterlädt wird im Applet (der Hauptthread, der aber wegen SwingUtilities.invokeLater(..) wartet und nix machen kann) wird ein netter Ladebalken angezeigt.

Wie gesagt, da der Hauptthread leider eingefroren ist kann er auch nicht den Ladebalken zeichnen. :(

Wie lasse ich den Hauptthread an einer bestimmten Stelle warten - aber er muss immer noch die validate() und JProgress Methoden durchführen können.

Thanks
undertaker
 

Wildcard

Top Contributor
undertaker hat gesagt.:
Während der Download-Thread das Bild herunterlädt wird im Applet (der Hauptthread, der aber wegen SwingUtilities.invokeLater(..) wartet und nix machen kann) wird ein netter Ladebalken angezeigt.
Wie jetzt? Du lässt den 'Haupthread', also den Thread der unter anderem die init Methode deines Applets durchläuft warten, bzw. syschronisierst ihn mit invokeLater? ???:L
Verstehe ich das richtig?
Am besten du zeigst etwas relevanten Code dazu.
 
U

undertaker

Gast
Gut, ich mache es anders:
Ich rufe die showPicImpl() Methode _direkt_ im selben Hauptthread auf.
Leider erscheint jetzt kein ProgressBalken mehr - trotz des this.validate().

Gibt es hierzu eine Möglichkeit? :/

(Code wurde bereits weiter oben gepostet)
 

Wildcard

Top Contributor
Ja, nur ist obiger Code ziemlich Kontextlos.
Obige Methode rufst du also jetzt aus dem Event Dispatcher Thread auf?
Ist klar das die Progressbar dann nicht mehr funktioniert, da ihr Thread ja blockiert ist.
So musst du's machen:
Diese zeitintensive Methode musst du in einen seperaten Worker-Thread auslagern.
Dieser Thread sollte eine Referenz auf die ProgressBar haben.
Wann immer du zum update der ProgressBar bereit bist rufst du (über die SwingUtilities mit invokeLater) setValue auf diesem auf.
 
U

undertaker

Gast
Code:
public class imgDownloader extends Thread {
    private URL shURL = null;
    private JProgressBar listener = null;
    /** Creates a new instance of imgDownloader */
    public imgDownloader(URL sURL, JProgressBar sListener) {
        this.shURL = sURL; //Die URL zum Downloaden
        this.listener = sListener; //Die JProgressBar vom Hauptthread
    }
    
    public BufferedImage download() throws IOException{ //Startet den Download und gibt dann das Image zurück.
    JProgressBar downProg = new JProgressBar(0, 100);
        downProg.setStringPainted(true);
        downProg.setString("Loading...");
        downProg.setForeground(Color.orange);
        downProg.setBackground(Color.WHITE);

        
//Download - Verbindung aufbauen:         

         URL getURL =  this.shURL;
         InputStream in;
         OutputStream out;
         URLConnection con = getURL.openConnection();
         
         con.setDoInput(true);
         con.connect();
         in = con.getInputStream();

         long downov = con.getContentLength();      
         ByteArrayOutputStream bout = new ByteArrayOutputStream();

         //Bytes downloaden:
        byte[] buf = new byte[5000];
        int nread;
        int navailable;
        int total = 0; //Menge an Bytes bisher gesendet
        int percentage = 0; //Percent done...
        int oldpercent = 0;
        byte[] filedata = new byte[con.getContentLength()];

          while((nread = in.read(buf, 0, buf.length)) > -1) {
            bout.write(buf,0,nread);
           total += nread; //Wieviel bereits gesendet?
            percentage = (int)( ( total * 100.0 ) / downov );
            if(oldpercent < percentage){
               System.out.println("Preview %: " + percentage);
               downProg.setString("Loading preview: "+percentage+"%");
               downProg.setValue(percentage);

              
               
            }
                oldpercent = percentage;
         }
 
 buf = null;
       
        ByteArrayInputStream imgds = new ByteArrayInputStream(bout.toByteArray());

        BufferedImage  img = ImageIO.read(imgds);
        
       

        

        
        return(img);
    }
    
}

So, das währe ja ein Anfang.
Wenn ich aber hier die JProgressBar aktualisiere (Instanzvariable Listener) - habe ich nicht dann auch denselben Effekt wie ich ihn bereits bei meinem vorherigen Versuch (runnable...) hatte?

Wie aktualisiere ich jetzt den GUI-Thread?
 

Wildcard

Top Contributor
Ich seh da nichtmal eine run Methode, was soll das bringen?
Was haben GUI Komponenten in einer Klasse extends Thread zu suchen? :shock:
Bist du dir sicher das du die Sache mit den Threads verstanden hast?
Sofern du des Englischen mächtig bist sollte dir das hier einiges klarer machen:
http://www.javalobby.org/eps/galbraith-swing-2/
 

Wildcard

Top Contributor
Nein, du darfst nichts was mit der GUI zu tun hat in einen Thread auslagern.
Nur der EDT (der Thread der deine init Aufruft) darf GUI Manipulationen durchführen.
Du musst deinen Code entsprechend ändern und den Arbeitsteil von der Anzeige trennen.
 
U

undertaker

Gast
Aha, jetzt leuchtet's mir ein!

Folgende Klasse lädt das Bild für mich runter:
Code:
public class imgDownloader extends Thread {
    private URL shURL = null;
    private JProgressBar listener = null;
    /** Creates a new instance of imgDownloader */
    public imgDownloader(URL sURL, JProgressBar sListener) {
        this.shURL = sURL; //Die URL zum Downloaden
        this.listener = sListener; //Die JProgressBar vom Hauptthread
    }

    
    public BufferedImage download() throws IOException{ //Startet den Download und gibt dann das Image zurück.
    JProgressBar downProg = new JProgressBar(0, 100);
        downProg.setStringPainted(true);
        downProg.setString("Loading...");
        downProg.setForeground(Color.orange);
        downProg.setBackground(Color.WHITE);

        
//Download - Verbindung aufbauen:         

         URL getURL =  this.shURL;
         InputStream in;
         OutputStream out;
         URLConnection con = getURL.openConnection();
         
         con.setDoInput(true);
         con.connect();
         in = con.getInputStream();

         long downov = con.getContentLength();      
         ByteArrayOutputStream bout = new ByteArrayOutputStream();

         //Bytes downloaden:
        byte[] buf = new byte[5000];
        int nread;
        int navailable;
        int total = 0; //Menge an Bytes bisher gesendet
        int percentage = 0; //Percent done...
        int oldpercent = 0;
        byte[] filedata = new byte[con.getContentLength()];

          while((nread = in.read(buf, 0, buf.length)) > -1) {
            bout.write(buf,0,nread);
           total += nread; //Wieviel bereits gesendet?
            percentage = (int)( ( total * 100.0 ) / downov );
            if(oldpercent < percentage){
               System.out.println("Preview %: " + percentage);
               downProg.setString("Loading preview: "+percentage+"%");
               downProg.setValue(percentage);

              
               
            }
                oldpercent = percentage;
         }
 
 buf = null;
       
        ByteArrayInputStream imgds = new ByteArrayInputStream(bout.toByteArray());

        BufferedImage  img = ImageIO.read(imgds);
        
       

        

        
        return(img);
    }
    
}


Wie mache ich jetzt etwas Threadartiges daraus?
Wie muss die run()-Methode aussehen und was soll diese übernehmen?

Wie kriegt der Thread
a) die Progessbar?
b) die URL, die er runterladen muss?

c) und wie kommt das Image wieder "nach Hause"?
 

Wildcard

Top Contributor
undertaker hat gesagt.:
Wie kriegt der Thread
a) die Progessbar?
b) die URL, die er runterladen muss?

c) und wie kommt das Image wieder "nach Hause"?
a) Kriegt er nicht. Du gibts ihm in Konstruktor eine Referenz auf eine ProgressBar mit. Die ProgressBar wird dann über die SwingUtilities geupdated.

b)Konstruktor oder setter

c)Referenz auf ein Objekt mit entsprechender set Methode.

d)ein Thread ohne run Methode ist kein Thread :wink:
 
U

undertaker

Gast
d)ein Thread ohne run Methode ist kein Thread icon_wink.gif

Was muss denn in die Run rein? :)
 

Wildcard

Top Contributor
Das was der Thread machen soll.
Dann rufst du Thread.start() auf, damit wird der Thread erzeugt und run wird aufgerufen.
 
U

undertaker

Gast
Code:
        try {            
            Thread imgDown = new imgDownloader(new URL(showurl),Progress) ;
            imgDown.start();
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }

Wie kriege ich das fertige Bild?

Die Klasse:
Code:
public class imgDownloader extends Thread {
    private URL shURL = null;
    private JProgressBar listener = null;
    /** Creates a new instance of imgDownloader */
    public imgDownloader(URL sURL, JProgressBar sListener) {
        this.shURL = sURL; //Die URL zum Downloaden
        this.listener = sListener; //Die JProgressBar vom Hauptthread
    }

    
    public BufferedImage download() throws IOException{ //Startet den Download und gibt dann das Image zurück.
    JProgressBar downProg = new JProgressBar(0, 100);
        downProg.setStringPainted(true);
        downProg.setString("Loading...");
        downProg.setForeground(Color.orange);
        downProg.setBackground(Color.WHITE);

        
//Download - Verbindung aufbauen:         

         URL getURL =  this.shURL;
         InputStream in;
         OutputStream out;
         URLConnection con = getURL.openConnection();
         
         con.setDoInput(true);
         con.connect();
         in = con.getInputStream();

         long downov = con.getContentLength();      
         ByteArrayOutputStream bout = new ByteArrayOutputStream();

         //Bytes downloaden:
        byte[] buf = new byte[5000];
        int nread;
        int navailable;
        int total = 0; //Menge an Bytes bisher gesendet
        int percentage = 0; //Percent done...
        int oldpercent = 0;
        byte[] filedata = new byte[con.getContentLength()];

          while((nread = in.read(buf, 0, buf.length)) > -1) {
            bout.write(buf,0,nread);
           total += nread; //Wieviel bereits gesendet?
            percentage = (int)( ( total * 100.0 ) / downov );
            if(oldpercent < percentage){
               System.out.println("Preview %: " + percentage);
               downProg.setString("Loading preview: "+percentage+"%");
               downProg.setValue(percentage);

              
               
            }
                oldpercent = percentage;
         }
 
 buf = null;
       
        ByteArrayInputStream imgds = new ByteArrayInputStream(bout.toByteArray());

        BufferedImage  img = ImageIO.read(imgds);
        
       

        

        
        return(img);
    }
    
    
    public void run(){
        try {
            download();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
}
 

Wildcard

Top Contributor
undertaker hat gesagt.:
Wie kriege ich das fertige Bild?
Wie bereits gesagt kannst du das fertige Bild per Setter an ein Referenziertes Objekt übergeben.
Weiterhin habe ich dir bereits gesagt das du GUI Komponenten nicht in einem Worker Thread verwenden darfst.
Mir willst du anscheinend nicht wirklich zuhören, daher lege ich dir nahe erstmal ein Tutorial zu machen oder etwas in der Javainsel zu lesen.
 
U

undertaker

Gast
Gut, ich hoffe es jetzt verstanden zu haben:


Jedesmal im WorkerThread wird der aktuelle Prozentestatus an den Hauptthread übergeben:
Code:
app.setProgressStatus(percentage); //Im Hauptthread - Funktion für das setzen der ProgressBar.
yield();

Das Bild wird jetzt zwar wunderbar heruntergeladen und nach dem Download im Applet angezeigt - ein Ladebalken erscheint aber nicht :(
 
U

undertaker

Gast
Ah!
Es geht jetzt!

Danke, danke für Deine Hilfe.

Ich sollte zukünftig alles mal gründlicher durchlesen :)
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben