Swing Java crashed nach Anzeige JOptionePane

Blakh

Bekanntes Mitglied
Hallo!
ich habe ein mir unverständliches Problem mit JOptionPane. Auf meinem Windows 7 Laptop passiert dieses:

Java(TM) Platform SE binary reagiert nicht

nachdem ich auf das OK des folgenden Dialogs klicke:
Java:
JOptionPane.showInternalMessageDialog(applet.getContentPane(),
						applet.getConnection().getReason(), "Error",  
						JOptionPane.ERROR_MESSAGE);

:bahnhof:

Das JOptionPane wird folgendermassen aufgerufen aufgerufen (es handelt sich um eine Innere Klasse):

Java:
	class ConnectActionListener implements ActionListener, Runnable {
		
		@Override
		public void actionPerformed(ActionEvent arg0) {

			Thread thread = new Thread(this);
			thread.start();

		}


		@Override
		public void run() {

		        ...
			
			if (applet.getConnection().getConState() != Connection.CONNECTED) {

				JOptionPane.showInternalMessageDialog(applet.getContentPane(),
						applet.getConnection().getReason(), "Error",  
						JOptionPane.ERROR_MESSAGE);
			}


		}

	}

Wenn ich das ganze im Debug-Modus laufen lassen, ist alles super. Auch auf meinem Windows XP - Rechner läuft das. Werden die JOptionPanes nicht aus einem separaten thread aufgerufen, crashed Java auch nicht.

Also zusammengefasst hängt sich Java auf:
separater Thread
Windows 7 64bit
eclipse IDE

Wenn irgendwer eine Idee hat, was da schief läuft, wäre das toll. Wenn ihr noch mehr Informationen braucht, welche ich vergessen habe, bitte fragen.

Danke
 
Ein KSKB könnte helfen. die showInternal-Methoden habe ich noch nicht verwendet - vielleicht gibt's da ein Problem, wenn die Component zu einem Applet führt? Schon mal die "nicht-interne" Variante versucht?
 
Danke für die Antwort. Also:

Das Problem tritt nicht bei showMessageDialog auf. Ich habe als Test mal folgendes geschrieben:

Java:
import javax.swing.GroupLayout;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Applet extends JApplet {

	@Override
	public void init() {

		JPanel panel = new JPanel();

		panel.setBounds(0, 0, 200, 200);

		//layout
		GroupLayout layout = new GroupLayout(getContentPane());
		getContentPane().setLayout(layout);
		layout.setHorizontalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel, 0,
				GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
		layout.setVerticalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel,
				GroupLayout.Alignment.TRAILING, 0, GroupLayout.DEFAULT_SIZE,
				Short.MAX_VALUE));
		
		new JOptionPaneThread();
		
		
	}
	
	/****************************************************************
	 * Inner class with separated Thread
	 ****************************************************************/

	class JOptionPaneThread implements  Runnable {
		

		public JOptionPaneThread() {

			Thread thread = new Thread(this);
			thread.start();

		}

		@Override
		public void run() {
			
			
			JOptionPane.showInternalMessageDialog(Applet.this.getContentPane(),
					"This is a test!", "Error",  
					JOptionPane.ERROR_MESSAGE);
		}

		

	}

}

Ergebnis: Es funktioniert. Also wo könnte es da denn Probleme geben?

Bei meinem Applet wird in dem Thread versucht eine Socket-Verbindung aufzubauen. Es werden Listener (Componenten wie ein Log-Fenster etc.) mitgeteilt, dass der Status der Verbindung sich geändert hat. Dann timed der Verbindungsversuch aus. Der Status der Verbindung ändert sich wieder (Listener reagieren wieder entsprechend) und dann wird das JOptionPane aufgerufen.

Ich bin total ratlos was da schief läuft. Ich wüsste auch nicht inwiefern sich da Threads in die Quere kommen können. Wenn ich debugge stürzt nichts ab. ???:L

Kann es sein, dass das InternalMessageDialog das Applet locked und nicht mehr freigeben kann irgendwie? Kenn mich da leider nicht mit Details aus.
 
Zuletzt bearbeitet:
Also es hängt sich einfach auf? Das GUI Blockiert? Hast du mal NACH dem Anzeigen des Dialogs sowas wie
System.out.println("Und weiter geht's...");
eingebaut, um zu sehen, ob er das noch macht, oder wirklich "mit dem Schließen" des Dialogs hängenbleibt?
 
Also ich sehe hier gleich 2 Probleme.
1. Laut API Doc sind die showInternal Methoden reserviert für JInternalFrames, allerdings benutzt du keine JInternalFrames
2. Swing ist nicht threadsafe, daher darfst du das ganze sowieso nicht aus einem Background Thread aufrufen.
 
@Macro13

Da der Thread ja auf eine Bestätigung wartet, wirds auch nicht weiter ausgeführt. Eben ist es jedoch passiert, dass es sich nicht aufgehangen hat. Hab aber nix am Code geändert.
Es blockiert alles. Das Fenster, wenn ich es mit Eclipse starte reagiert nicht mehr und dann kommt die Meldung, welche ich am Anfang gepostet habe.

@Gastredner Keine Fehlermeldung :/, also wird das nichts bringen.

@Wildcard
Zu 1. ... wo steht das? Hier konnt ich nix finden: JOptionPane (Java 2 Platform SE v1.4.2)

zu 2. Und wie soll ich dann sowas machen? Wenn ich kein extra Thread mache, dann blockiert ja die GUI. Desweiteren brauch ich ja Background Threads um z.b. auf Servernachrichten zu warten. Nicht threadsafe bedeutet, dass solche Dinge passieren?

Danke
 
Nicht threadsafe bedeutet, dass solche Dinge passieren?
Wenn man etwas falsch macht, ja. Im Moment meine ich aber, dass man diese show*Dialog-Methoen (da es sich um Modale Dialoge handelt) von jedem beliebigen Thread aufrufen darf.

Nochmal: Hast du schonmal die Variante ohne "Internal" versucht?
 
OptionPanes muessen doch auch aus dem EDT geoeffnet werden, oder? Probier mal:
Java:
SwingUtilities.invokeLater( new Runnable() {
   @Override
   public void run() {
     JOptionPane.show....
   }
}

Zudem:
Each of these methods also comes in a showInternalXXX flavor, which uses an internal frame to hold the dialog box (see JInternalFrame). Multiple convenience methods have also been defined -- overloaded versions of the basic methods that use different parameter lists.
 
Nicht threadsafe bedeutet, dass solche Dinge passieren?
Wenn man etwas falsch macht, ja. Im Moment meine ich aber, dass man diese show*Dialog-Methoen (da es sich um Modale Dialoge handelt) von jedem beliebigen Thread aufrufen darf.

Nochmal: Hast du schonmal die Variante ohne "Internal" versucht?

Das hatte ich schon beantwortet -> Läuft so wunderbar. Kein Crash. Der Meinung war ich auch... für mich heißt Threadsafe, man muss selbst aufpassen, dass Swing sich nicht verwurschtelt.


@ schalentier

"uses an internal frame to hold the dialog box (see JInternalFrame)"

InternalDialogs sind InternalFrames ... aber das heißt für mich nicht, dass die nur in Verbindung mit jenen evrwendet werden sollen.

Ich werd das mal ausprobieren mit SwingUtilities. Die Idee hatte ich gestern nacht auch noch, abr es war schon spät 🙂.
 
OptionPanes muessen doch auch aus dem EDT geoeffnet werden, oder? Probier mal:
Java:
SwingUtilities.invokeLater( new Runnable() {
   @Override
   public void run() {
     JOptionPane.show....
   }
}

Funktioniert.

Es handelt sich wohl tatsächlich um irgendeine Art DeadLock, der sich nicht mehr löst. Anscheinend sollte ich nochmal meinen Code ergänzen und überall das einfügen, wo solche Sachen von Background-Threads geöffnet werden. Beim nächsten mal weiß ich Bescheid.

Danke
 
Zuletzt bearbeitet:
OptionPanes muessen doch auch aus dem EDT geoeffnet werden, oder?

Eigentlich ist das ja der Standard bei Swing. Bei JOptionPanes war ich mir da aber nicht sicher. Unter Dialog#setVisible steht nämlich
It is OK to call this method from the event dispatching thread because the toolkit ensures that other events are not blocked while this method is blocked.

Das ist ja dann vergleichbar mit der Aussage: "Es ist OK, bei grün über eine Ampel zu fahren" :autsch: (Ja, gut, einmal muss es gesagt werden, aber ... naja 😳 ). Hab' jetzt mal im Code geguckt: Anscheinend gehen die ganzen show*-Methoden wirklich davon aus, vom EDT aus aufgerufen zu werden. Müßte mal schauen, ob man einen beliebigen Thread mit so einem Dialog zu blockieren könnte, wenn man mit invokeAndWait rumfrickelt... komisch komisch...
 
Vollständigkeitshalber: So hab ich es geändert (Schema):

Java:
import javax.swing.GroupLayout;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Applet extends JApplet {

	@Override
	public void init() {

		JPanel panel = new JPanel();

		panel.setBounds(0, 0, 200, 200);

		//layout
		GroupLayout layout = new GroupLayout(getContentPane());
		getContentPane().setLayout(layout);
		layout.setHorizontalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel, 0,
				GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
		layout.setVerticalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel,
				GroupLayout.Alignment.TRAILING, 0, GroupLayout.DEFAULT_SIZE,
				Short.MAX_VALUE));
		
		new JOptionPaneThread();
		
		
	}
	
	/****************************************************************
	 * Inner class with separated Thread
	 ****************************************************************/

	class JOptionPaneThread implements  Runnable {
		

		public JOptionPaneThread() {

			Thread thread = new Thread(this);
			thread.start();

		}

		@Override
		public void run() {
			
			SwingUtilities.invokeLater( new Runnable() {
			   @Override
			   public void run() {
			        JOptionPane.showInternalMessageDialog(Applet.this.getContentPane(),
					"This is a test!", "Error",  
					JOptionPane.ERROR_MESSAGE);
                                }
                          });
		}

		

	}

}
 
Jo, ich hab mal irgendwo gelesen, dass es da eine API Aenderung im Swing Code gegeben hat. Vor einer bestimmten Java Version konnte man JOptionPane Dialoge wohl von ueberall aus oeffnen, nach der Version nur noch vom EDT aus. Aber weiss nich mehr genau und hab grad keine Zeit das genauer zu recherchieren. Hauptsache es funktioniert jetzt :-D
 
Zu 1. ... wo steht das? Hier konnt ich nix finden: JOptionPane (Java 2 Platform SE v1.4.2)
Steht in der Klassenbeschreibung
JOptionPane (Java Platform SE 6)
Each of these methods also comes in a showInternalXXX flavor, which uses an internal frame to hold the dialog box (see JInternalFrame). Multiple convenience methods have also been defined -- overloaded versions of the basic methods that use different parameter lists.
Weiterhin steht dort das JOptionPane nicht threadsafe ist (generell steht in der Swing Doku ganz explizit das keine Methode Threadsafe ist, ausser es steht explizit dabei).
Warning: Swing is not thread safe. For more information see Swing's Threading Policy.
für mich heißt Threadsafe, man muss selbst aufpassen, dass Swing sich nicht verwurschtelt.
Nein, das heißt es nicht. Es heißt das es falsch ist Swing Methoden aus einem anderen Thread als dem EDT aufzurufen. Das die Swing API an dieser Stelle leider grottig implementiert ist und nicht schon beim Versuch eine Exception wirft wie es zB bei SWT gemacht wird, lässt sich nun leider nicht mehr ändern.
Vollständigkeitshalber: So hab ich es geändert (Schema):
Was du da mit dem JOptionPaneThread machst ist völlig Banane, lösch das Ding.
Wenn du an der Stelle an der du zur Zeit den JOptionPaneThread instanzierst diesen Code einfügst, hast du den selben Effekt:
Java:
            SwingUtilities.invokeLater( new Runnable() {
               @Override
               public void run() {
                    JOptionPane.showMessageDialog(Applet.this.getContentPane(),
                    "This is a test!", "Error",  
                    JOptionPane.ERROR_MESSAGE);
                                }
                          });
 
Steht in der Klassenbeschreibung
JOptionPane (Java Platform SE 6)

Weiterhin steht dort das JOptionPane nicht threadsafe ist (generell steht in der Swing Doku ganz explizit das keine Methode Threadsafe ist, ausser es steht explizit dabei).

Das heißt für mich, dass die Dialog-Box in einem JInternalFrame daherkommt und nicht, dass es nur in Verbindung mit einem JInternalFrame funktioniert. Das erklärt auch das Verhalten des Dialogfensters (es überlagert nicht komplett den Browser z.B., sondern bleibt im Applet-Fenster).

Was du da mit dem JOptionPaneThread machst ist völlig Banane, lösch das Ding.
Wenn du an der Stelle an der du zur Zeit den JOptionPaneThread instanzierst diesen Code einfügst, hast du den selben Effekt:
Java:
            SwingUtilities.invokeLater( new Runnable() {
               @Override
               public void run() {
                    JOptionPane.showMessageDialog(Applet.this.getContentPane(),
                    "This is a test!", "Error",  
                    JOptionPane.ERROR_MESSAGE);
                                }
                          });

Hab ich nicht.... denn der EDT ist dann mit dem Ausführen des run()-Methode beschäftigt. Daraus folgt, dass die GUI für 5 - 10 Sekunden quasi blockiert ist und nix passiert.

Dass mein Code so quatschist, ist ja richtig, wenn es nur darum geht im dem separaten Thread das Dialog-Fenster zu öffnen, aber nicht, wenn in dem Thread versucht wird eine Verbindung aufzubauen. Deshalb habe ich hingeschrieben (Schema) 🙂

MfG
 
Das heißt für mich, dass die Dialog-Box in einem JInternalFrame daherkommt und nicht, dass es nur in Verbindung mit einem JInternalFrame funktioniert. Das erklärt auch das Verhalten des Dialogfensters (es überlagert nicht komplett den Browser z.B., sondern bleibt im Applet-Fenster).
Ja, du hast recht. Habe ich beim ersten Lesen falsch verstanden.
Hab ich nicht.... denn der EDT ist dann mit dem Ausführen des run()-Methode beschäftigt. Daraus folgt, dass die GUI für 5 - 10 Sekunden quasi blockiert ist und nix passiert.
Du verstehst das invokeLater falsch. invokeLater schiebt ein Runnable auf die Queue des EDTs damit dieser sich später darum kümmert. Das Runnable läuft dabei nicht in einem anderen Thread sondern im EDT, genau das ist ja auch sinn der Sache. Sprich: während das Runnable läuft blockiert der EDT.
Die Methode von der aus du zur Zeit den Thread startest läuft ebenfalls im EDT. Der EDT ist nur ein Thread, sprich er kann die Queue nicht abarbeiten solange deine Methode noch nicht fertig ist.
Mit invokeLater pakst du das Runnable auf die Queue, es wird aber erst ausgeführt nachdem deine Methode fertig ist, daher kannst du dir den separaten Thread auch gleich sparen.
 
Du verstehst das invokeLater falsch. invokeLater schiebt ein Runnable auf die Queue des EDTs damit dieser sich später darum kümmert. Das Runnable läuft dabei nicht in einem anderen Thread sondern im EDT, genau das ist ja auch sinn der Sache. Sprich: während das Runnable läuft blockiert der EDT.
Die Methode von der aus du zur Zeit den Thread startest läuft ebenfalls im EDT. Der EDT ist nur ein Thread, sprich er kann die Queue nicht abarbeiten solange deine Methode noch nicht fertig ist.
Mit invokeLater pakst du das Runnable auf die Queue, es wird aber erst ausgeführt nachdem deine Methode fertig ist, daher kannst du dir den separaten Thread auch gleich sparen.

Ich pack das Runnable aber am Ende des neuen Threads, wenn die ganze Connection-Versuche gescheitert sind in die Queue. Damit ist der EDT eben nicht blockiert.

Ich merke doch den Unterschied bei meinem Programm. Mach ich es so wie in meinem Beispiel -> Ich starte Verbindungsaufbau ... ich sehe Status-label zeigt connecting ..., ich kann andere Menüpunkte aufrufen etc. Mach ich es so wie du vorschlägst: EDT blockiert. Keine Interaktion mehr möglich.

Java:
import javax.swing.GroupLayout;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Applet extends JApplet {

	@Override
	public void init() {

		JPanel panel = new JPanel();

		panel.setBounds(0, 0, 200, 200);

		//layout
		GroupLayout layout = new GroupLayout(getContentPane());
		getContentPane().setLayout(layout);
		layout.setHorizontalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel, 0,
				GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
		layout.setVerticalGroup(layout.createParallelGroup(
				GroupLayout.Alignment.LEADING).addComponent(panel,
				GroupLayout.Alignment.TRAILING, 0, GroupLayout.DEFAULT_SIZE,
				Short.MAX_VALUE));
		
		new JOptionPaneThread();
		
		
	}
	
	/****************************************************************
	 * Inner class with separated Thread
	 ****************************************************************/

	class JOptionPaneThread implements  Runnable {
		

		public JOptionPaneThread() {

			Thread thread = new Thread(this);
			thread.start();

		}

		@Override
		public void run() {

                     /**   Mache hier langwierige Dinge **/
			
			SwingUtilities.invokeLater( new Runnable() {
			   @Override
			   public void run() {
			        JOptionPane.showInternalMessageDialog(Applet.this.getContentPane(),
					"This is a test!", "Error",  
					JOptionPane.ERROR_MESSAGE);
                                }
                          });
		}

		

	}

}
 
Zuletzt bearbeitet:

Zurück
Oben