Es MVC en Swing Thread Safe

Estoy tratando de tocar los límites de la architecture MVC en Swing, pero como probé todo, todos (desde SwingWorker o Runnable#Thread ) están hechos en EDT

Mis preguntas:

  • ¿Hay algunos límites o depende estrictamente por orden de las implementaciones ( SwingWorker en SwingWorker o Runnable#Thread )?

  • ¿Es limitado el método JComponent # Thread Safe o no?

  • característica esencial de una architecture MVC en Swing,?

  • Cª. Re-Layout del contenedor?

Nota: para mi SSCCE tomo uno de los mejores ejemplos de HFOE , y tal vez al mantener estos principios estrictamente no es posible crear ninguna falla de EDT o locking de la GUI.

enter image description hereenter image description hereenter image description hereenter image description here

 import java.awt.BorderLayout; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.LinkedList; import java.util.Queue; import javax.swing.*; public class MVC_ProgressBarThread { private MVC_ProgressBarThread() { MVC_View view = new MVC_View(); MVC_Model model = new MVC_Model(); MVC_Control control = new MVC_Control(view, model); view.setControl(control); JFrame frame = new JFrame("MVC_ProgressBarThread"); frame.getContentPane().add(view); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { MVC_ProgressBarThread mVC_ProgressBarThread = new MVC_ProgressBarThread(); } }); } } class MVC_View extends JPanel { private static final long serialVersionUID = 1L; private MVC_Control control; private JProgressBar progressBar = new JProgressBar(); private JButton startActionButton = new JButton("Press Me and Run this Madness"); private JLabel myLabel = new JLabel("Nothing Special"); public MVC_View() { startActionButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { buttonActionPerformed(); } }); JPanel buttonPanel = new JPanel(); startActionButton.setFocusPainted(false); buttonPanel.add(startActionButton); setLayout(new BorderLayout(10, 10)); add(buttonPanel, BorderLayout.NORTH); progressBar.setStringPainted(true); add(progressBar, BorderLayout.CENTER); myLabel.setIcon(UIManager.getIcon("OptionPane.questionIcon")); myLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); add(myLabel, BorderLayout.SOUTH); } public void setControl(MVC_Control control) { this.control = control; } private void buttonActionPerformed() { if (control != null) { control.doButtonAction(); } } public void setProgress(int progress) { progressBar.setValue(progress); } public void setProgressLabel(String label) { progressBar.setString(label); } public void setIconLabel(Icon icon) { myLabel.setIcon(icon); } public void start() { startActionButton.setEnabled(false); } public void done() { startActionButton.setEnabled(true); setProgress(100); setProgressLabel(" Done !!! "); setIconLabel(null); } } class MVC_Control { private MVC_View view; private MVC_Model model; public MVC_Control(final MVC_View view, final MVC_Model model) { this.view = view; this.model = model; model.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pce) { if (MVC_Model.PROGRESS.equals(pce.getPropertyName())) { view.setProgress((Integer) pce.getNewValue()); } if (MVC_Model.PROGRESS1.equals(pce.getPropertyName())) { view.setProgressLabel((String) pce.getNewValue()); } if (MVC_Model.PROGRESS2.equals(pce.getPropertyName())) { view.setIconLabel((Icon) pce.getNewValue()); } } }); } public void doButtonAction() { view.start(); SwingWorker swingworker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { model.reset(); model.startSearch(); return null; } @Override protected void done() { view.done(); } }; swingworker.execute(); } } class MVC_Model { public static final String PROGRESS = "progress"; public static final String PROGRESS1 = "progress1"; public static final String PROGRESS2 = "progress2"; private static final int MAX = 11; private static final long SLEEP_DELAY = 1000; private int progress = 0; private String label = "Start"; private PropertyChangeSupport pcs = new PropertyChangeSupport(this); private PropertyChangeSupport pcs1 = new PropertyChangeSupport(this); private PropertyChangeSupport pcs2 = new PropertyChangeSupport(this); private final String[] petStrings = {"Bird", "Cat", "Dog", "Rabbit", "Pig", "Fish", "Horse", "Cow", "Bee", "Skunk"}; private int index = 1; private Queue iconQueue = new LinkedList(); private Icon icon = (UIManager.getIcon("OptionPane.questionIcon")); public void setProgress(int progress) { int oldProgress = this.progress; this.progress = progress; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS, oldProgress, progress); pcs.firePropertyChange(evt); } public void setProgressLabel(String label) { String oldString = this.label; this.label = label; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS1, oldString, label); pcs1.firePropertyChange(evt); } public void setIconLabel(Icon icon) { Icon oldIcon = this.icon; this.icon = icon; PropertyChangeEvent evt = new PropertyChangeEvent(this, PROGRESS2, oldIcon, icon); pcs2.firePropertyChange(evt); } public void reset() { setProgress(0); } public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); pcs1.addPropertyChangeListener(listener); pcs2.addPropertyChangeListener(listener); } public void startSearch() { iconQueue.add(UIManager.getIcon("OptionPane.errorIcon")); iconQueue.add(UIManager.getIcon("OptionPane.informationIcon")); iconQueue.add(UIManager.getIcon("OptionPane.warningIcon")); iconQueue.add(UIManager.getIcon("OptionPane.questionIcon")); for (int i = 0; i < MAX; i++) { int newValue = (100 * i) / MAX; setProgress(newValue); setProgressLabel(petStrings[index]); index = (index + 1) % petStrings.length; setIconLabel(nextIcon()); try { Thread.sleep(SLEEP_DELAY); } catch (InterruptedException e) { } } } private Icon nextIcon() { Icon icon1 = iconQueue.peek(); iconQueue.add(iconQueue.remove()); return icon1; } } 

Esto es demasiado largo para un comentario …

Primero, y esto no está relacionado con el rest de esta respuesta: hay muchos MVC diferentes y el que usaste en ese fragmento de código que publicaste aquí no es el mismo que el utilizado en el artículo al que vinculaste: http: / /www.oracle.com/technetwork/articles/javase/mvc-136693.html

El artículo señala correctamente que es solo “una implementación de MVC común” (una en la que la vista registra a un oyente que escucha los cambios del modelo). Su implementación es un tipo diferente de MVC, donde el controlador registra a un oyente que escucha los cambios del modelo y luego actualiza la vista.

No es que haya nada de malo en eso: hay muchos tipos diferentes de MVC (*).

(Otra pequeña advertencia … Su vista es consciente de su controlador en su ejemplo, que es un poco raro: hay otras maneras de hacer lo que está haciendo sin necesidad de “alimentar” el controlador a la vista como lo hace con su setControl (…) dentro de su MVCView ).

Pero de todos modos … Básicamente, casi siempre estás modificando la GUI desde fuera del EDT (cosa que no deberías hacer):

 public void setIconLabel(final Icon icon) { myLabel.setIcon(icon); } 

Puede verificarlo agregando esto:

 System.out.println("Are we on the EDT? " + SwingUtilities.isEventDispatchThread()); 

Esto se debe a que finalmente estás haciendo estas actualizaciones desde tu subproceso SwingWorker (el subproceso SwingWorker se ejecuta fuera del EDT: básicamente es el punto de un trabajador de Swing).

Preferiría actualizar la GUI del EDT, haciendo algo como esto:

 public void setIconLabel(final Icon icon) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { myLabel.setIcon(icon); } }); }