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:
- Suspender el
Thread
que llamó el método! - 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 Thread
s, normalmente necesita algún tipo de sincronización (porque tiene dos Thread
s: el EDT y el creado). Necesitará esta sincronización porque los dos Thread
s (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 Thread
s 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:
- No tiene una condición para la terminación normal del
Thread
. Debería tener, por ejemplo, una condición en lugar detrue
en elwhile
bucle para indicar cuándo debemos salir delThread
graciosamente. - 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 enThread.sleep
para darte algo de tiempo libre, cuando en cambio deberías haber usado unScheduledExecutorService
o unjava.util.Timer
para programar a tarifa fija la operación deseada.
También tenga en cuenta que necesita sincronizar aquí porque tiene dos Thread
s (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 JRadioButton
s (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.