Saltar al contenido

Cómo iniciar / reanudar y detener / pausar un hilo dentro del oyente de acción en java

Basta ya de indagar por todo internet porque estás al espacio adecuado, contamos con la respuesta que buscas pero sin liarte.

Solución:

El código, tal como se proporciona, no imprimirá nada. Tampoco se compilará, necesitas arreglar private static Thread; decir private static Thread thr;.

De todos modos, esto puede funcionar o no, dependiendo, ya que el código carece de sincronización. Esto significa que los cambios realizados en una variable en un hilo no necesitan ser visibles en otro. Si tiene una sola variable establecida en false inicialmente, y luego configúrelo en true en un hilo, un segundo hilo todavía puede ver su valor en caché de false.

Intenta hacer tu boolean variables volatile y vea si funciona, pero una respuesta real es leer sobre la sincronización de subprocesos, por ejemplo, en el Tutorial de Java

los thr.wait() call hará lo siguiente:

  1. Suspender el Thread que llamó el método!
  2. Suelte las cerraduras Thread (que llamó al método) actualmente se mantiene.

El correspondiente notify (o notifyAll) la llamada al método debe realizarse para el mismo objeto exacto (es decir, thr.notify() o thr.notifyAll()) que suspendió el Thread queremos continuar.

Observe que el oyente de acciones actionPerformed se llama al método en el subproceso de envío de eventos (EDT para abreviar) (que en sí mismo es un Thread). Es decir, al hacer clic en el end botón, el actionPerformed se llama en el EDT y luego llama thr.wait() en él, lo que significa que suspendes el EDT! En Swing, hasta donde yo sé, casi todas las operaciones relacionadas con eventos tienen lugar en el EDT. Eso significa que si se ejecuta en el EDT, entonces bloquea otras operaciones, como recibir eventos de clics de botones, movimiento y desplazamiento del mouse, etc. En resumen, bloquear el EDT significa que la GUI no responde.

Aparte de eso, thr.wait() llamar (así como thr.notify() y thr.notifyAll()) debe hacerse dentro de un synchronized (thr) ... cuadra.

Si quieres interactuar con un Thread diferente al EDT (como al usar el Thread constructores, un ExecutorService, a SwingWorker etc …), y también hacer una comunicación entre los dos Threads, normalmente necesita algún tipo de sincronización (porque tiene dos Threads: el EDT y el creado). Necesitará esta sincronización porque los dos Threads (para comunicarse) van a compartir [a reference to] la misma variable. En tu caso es el print bandera que debe compartirse; uno Thread (el EDT) modificará la bandera, de acuerdo con el botón que se presionó, mientras que el otro Thread (el construido con una instancia de la clase Example Cuál es el Runnable) llamado thr, leerá la bandera repetidamente después de un intervalo / tiempo y luego hará el trabajo de impresión en System.out.

Note también, que el print bandera es una static propiedad de la clase Example, pero necesitas una instancia de clase para el Threads para sincronizar. Así que parece que ibas a utilizar el Example instancia de clase llamada thr para esto.

Tomemos, por ejemplo, el siguiente código:

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;

public class ThreadMain 
    
    private static class PrintingThread extends Thread 
        
        private boolean print;
        
        public PrintingThread() 
            print = false;
        
        
        public synchronized void keepPrinting() 
            print = true;
            notifyAll();
        
        
        public synchronized void pausePrinting() 
            print = false;
        
        
        @Override
        public void run() 
            try 
                while (true)  //You should add an end condition here, in order to let the Thread shutdown gracefully (other than interrupting it).
                    synchronized (this) 
                        if (!print)
                            wait();
                    
                    System.out.println("Printing...");
                    Thread.sleep(500);
                
            
            catch (final InterruptedException ix) 
                System.out.println("Printing interrupted.");
            
        
    
    
    private static void createAndShowGUI() 
        
        final PrintingThread printingThread = new PrintingThread();
        printingThread.start();
        
        final JRadioButton start = new JRadioButton("Print"),
                           stop = new JRadioButton("Pause", true);
        
        start.addActionListener(e -> printingThread.keepPrinting());
        stop.addActionListener(e -> printingThread.pausePrinting());
        
        /*Creating a button group and adding the two JRadioButtons, means that when
        you select the one of them, the other is going to be unselected automatically.
        The ButtonGroup instance is then going to be maintained in the model of each
        one of the buttons (JRadioButtons) that belong to the group, so you don't need
        to keep a reference to group explicitly in case you worry it will get Garbadge
        Collected, because it won't.*/
        final ButtonGroup group = new ButtonGroup();
        group.add(start);
        group.add(stop);
        
        final JPanel contentsPanel = new JPanel(); //FlowLayout by default.
        contentsPanel.add(start);
        contentsPanel.add(stop);
        
        final JFrame frame = new JFrame("Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contentsPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    
    
    public static void main(final String[] args) 
        
        //EDT related code should be called on the EDT..
        SwingUtilities.invokeLater(ThreadMain::createAndShowGUI);
    

Puedes ver aquí que creé un personalizado Thread y anulado run método para imprimir repetidamente System.out después de un intervalo / tiempo de 500 ms. El ciclo nunca terminará, a menos que el Thread se interrumpe. Sin embargo, no debe usarse como un buen ejemplo de implementación de lo que está intentando, porque:

  1. No tiene una condición para la terminación normal del Thread. Debería tener, por ejemplo, una condición en lugar de true en el while bucle para indicar cuándo debemos salir del Thread graciosamente.
  2. Llama Thread.sleep en el lazo. Esto se considera una mala práctica hasta donde yo sé, porque este es el caso generalmente cuando necesita hacer una operación repetidamente y confiar en Thread.sleep para darte algo de tiempo libre, cuando en cambio deberías haber usado un ScheduledExecutorService o un java.util.Timer para programar a tarifa fija la operación deseada.

También tenga en cuenta que necesita sincronizar aquí porque tiene dos Threads (el EDT y el PrintingThread). Lo digo de nuevo porque en el siguiente ejemplo vamos a utilizar simplemente el propio EDT para realizar la impresión (porque la impresión en System.out un solo mensaje no va a ser demasiado largo en este caso), que es otra implementación de muestra de lo que está tratando de hacer. Para programar la operación a una tarifa fija en el propio EDT, vamos a utilizar el javax.swing.Timer que existe para tal propósito.

El código:

import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TimerMain 
    
    private static void createAndShowGUI() 
        
        //Constructs a Timer such that, when running, every 500ms prints the desired message:
        final Timer printingTimer = new Timer(500, e -> System.out.println("Printing..."));
        
        /*The Timer is going to repeat events (ie call all its
        ActionListeners repeatedly)... This will simulate a loop.*/
        printingTimer.setRepeats(true);
        
        /*Coalescing means that events fast enough are going to be merged to one
        event only, and we don't want that in this case, so we set it to false:*/
        printingTimer.setCoalesce(false);
        
        final JRadioButton start = new JRadioButton("Print"),
                           stop = new JRadioButton("Pause", true);
        
        start.addActionListener(e -> printingTimer.restart());
        stop.addActionListener(e -> printingTimer.stop());
        
        /*Creating a button group and adding the two JRadioButtons, means that when
        you select the one of them, the other is going to be unselected automatically.
        The ButtonGroup instance is then going to be maintained in the model of each
        one of the buttons (JRadioButtons) that belong to the group, so you don't need
        to keep a reference to group explicitly in case you worry it will get Garbadge
        Collected, because it won't.*/
        final ButtonGroup group = new ButtonGroup();
        group.add(start);
        group.add(stop);
        
        final JPanel contentsPanel = new JPanel(); //FlowLayout by default.
        contentsPanel.add(start);
        contentsPanel.add(stop);
        
        final JFrame frame = new JFrame("Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contentsPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    
    
    public static void main(final String[] args) 
        
        //EDT related code should be called on the EDT...
        SwingUtilities.invokeLater(TimerMain::createAndShowGUI);
    

los javax.swing.Timer delega el propósito del ciclo.

También observe aquí, no usamos el synchornized palabra clave, porque no es necesario, porque todo el código se ejecuta en el EDT.

SwingUtilities.invokeLater es solo un pequeño método para invocar un Runnable en el EDT en algún momento en el futuro. Así que también necesitamos invocar la creación del JFrame, los JPanel y el JRadioButtons (o simplemente llame al createAndShowGUI) en el EDT, porque es un código relacionado con EDT (por ejemplo, ¿qué pasa si se activa un evento mientras se agrega el panel al marco? …).

Agregué algunos comentarios en el código para ayudar con otras cosas relacionadas con los ejemplos mostrados.

Déjame saber en los comentarios cualquier duda que pueda surgir, y actualizaré mi respuesta lo antes posible.

Te mostramos reseñas y valoraciones

Si te sientes estimulado, tienes el poder dejar un artículo acerca de qué le añadirías a este enunciado.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *