Come mettere in Pausa e Riprendere un Thread in Java da un altro Thread

Sto scrivendo un’applicazione con Java Swing. Ciò di cui ho bisogno è una procedura in cui riesco a smettere di “elaborazione” del filo utilizzando un pulsante nell’interfaccia grafica.

Qui un semplice progetto si è concentrato su quello che ho bisogno di

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTextArea;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Nikola
 */
public class Main extends javax.swing.JFrame
{
    private MyThread THREAD;

    public Main()
    {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    //<editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();
        jButton2 = new javax.swing.JButton();
        jButton3 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Pause Thread");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane1.setViewportView(jTextArea1);

        jButton2.setText("Resume Thread");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jButton3.setText("Start Thread");
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButton3)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 63, Short.MAX_VALUE)
                        .addComponent(jButton2)
                        .addGap(18, 18, 18)
                        .addComponent(jButton1))
                    .addComponent(jScrollPane1))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 244, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(jButton2)
                    .addComponent(jButton3))
                .addContainerGap())
        );

        pack();
    }//</editor-fold>

    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt)
    {
        THREAD = new MyThread(jTextArea1);
        THREAD.start();
    }

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
    {
        try
        {
            THREAD.pauseThread();
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
    }

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt)
    {
        THREAD.resumeThread();
    }

    public static void main(String args[])
    {
        /*
         * Set the Nimbus look and feel
         */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /*
         * If Nimbus (introduced in Java SE 6) is not available, stay with the
         * default look and feel. For details see
         * http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try
        {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
            {
                if ("Nimbus".equals(info.getName()))
                {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }
        catch (ClassNotFoundException ex)
        {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (InstantiationException ex)
        {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (IllegalAccessException ex)
        {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (javax.swing.UnsupportedLookAndFeelException ex)
        {
            java.util.logging.Logger.getLogger(Main.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /*
         * Create and display the form
         */
        java.awt.EventQueue.invokeLater(new Runnable()
        {

            public void run()
            {
                new Main().setVisible(true);
            }
        });
    }
    //Variables declaration - do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea jTextArea1;
    //End of variables declaration
}

class MyThread extends Thread
{
    JTextArea area;

    private final Object lock = new Object();

    public MyThread(JTextArea area)
    {
        super();
        this.area = area;
    }

    @Override
    public void run()
    {
        for(int i=0 ; ; i++)
            area.setText(i+"");
    }

    public void pauseThread() throws InterruptedException
    {
        synchronized(lock)
        {
            lock.wait();
        }
    }

    public void resumeThread()
    {
        synchronized(lock)
        {
            lock.notify();
        }
    }
}

La domanda è semplice:
Nell’applicazione reale, l’utente impostare alcune opzioni e quindi avviare il thread che fa l’elaborazione dei dati selezionati.

Voglio dare una “pausa” in modo che l’utente può interrompere temporaneamente l’elaborazione e rendere il bisogno di controllare e dopo di che è possibile riprendere il funzionamento.

Nel modo In cui ho codificato è la grafica thread che si fermano, e non la “elaborazione”.

Se si esegue il mio codice di esempio e premere il tasto “Start” textarea inizia a contare. Il risultato finale di cui ho bisogno è che quando premo il pulsante “Pausa” il thread di andare a “dormire” e il conteggio si ferma, quando si preme il pulsante “Riprendi” il filo “si sveglia” e il conteggio nell’area di testo inizia contro di conte.

OriginaleL’autore Deviling Master | 2012-08-16

2 Replies
  1. 16

    Non è possibile definitivamente pausa di un thread di un altro nel modo che vogliono.

    Quello che dovete fare, invece, è segnale che l’altro thread dovrebbe interrompere, impostando una sorta di bandiera. Il thread in questione deve avere la logica per verificare questa bandiera e di mettere in pausa il suo lavoro quando succede.

    Quindi, in questo caso particolare, forse cambiare MyThread come segue:

    class MyThread extends Thread {
    
        private volatile boolean running = true; //Run unless told to pause
    
        ...
    
        @Override
        public void run()
        {
            for(int i=0 ; ; i++)
            {
                //Only keep painting while "running" is true
                //This is a crude implementation of pausing the thread
                while (!running)
                    yield;
    
                area.setText(i+"");
        }
    
        public void pauseThread() throws InterruptedException
        {
            running = false;
        }
    
        public void resumeThread()
        {
            running = true;
        }
    
    }

    Questo è un semplice esempio che per brevità utilizza una sorta di spinlock, piuttosto che una corretta basata sul monitor del sonno. Speriamo, però comunica l’idea di come puoi usare un flag per controllare la sospensione del thread.

    Di notare che se si stavano facendo alcuni long-running serie di passaggi all’interno di un blocco, invece di solo il setText chiamata, sarebbe buona norma verificare Thread.currentThread().interrupted() tra ciascuno dei passi e chiudere il ciclo se il itnerrupt flag è impostato. Questo è sostanzialmente quello che il built-in blocco metodi (ad esempio, I/O) fare in modo che essi possono essere interrotti da altri thread – dal running bandiera è solo controllati uno per ciclo, non fa molto bene impostato, se ogni ciclo dura 20 minuti.

    resumeThread() non funziona, perché pauseThread() lascerà il ciclo for.
    Nell’operazione thread ci sono un SACCO di procedure che vengono eseguite e NON so quando l’utente preme il pulsante di pausa. Mi rifiuto di credere che in ogni metodo eseguito l’operazione di filettatura devo aggiungere un valore booleano per una condizione di stop. Ho bisogno di un esterno di comando per mettere in pausa il thread, non una interna
    Le tue ipotesi sono sbagliate. Non c’è modo per un thread di sospendere un altro thread. Il meglio che puoi fare è il segnale di una pausa.
    concordato, è stato un errore da parte mia, che ho affrontato.
    Solo se stesso, può sospendere un thread? No segnali esterni ammessi?

    OriginaleL’autore Andrzej Doyle

  2. 3

    Provare come questo:

    class MyThread extends Thread {
        JTextArea area;
        private final Object GUI_INITIALIZATION_MONITOR = new Object();
        private boolean pauseThreadFlag = false;
    
        public MyThread(JTextArea area) {
            super();
            this.area = area;
        }
    
        @Override
        public void run() {
            for(int i=0 ; ; i++) {
                checkForPaused();
                area.setText(i+"");
            }
        }
    
        private void checkForPaused() {
            synchronized (GUI_INITIALIZATION_MONITOR) {
                while (pauseThreadFlag) {
                    try {
                        GUI_INITIALIZATION_MONITOR.wait();
                    } catch (Exception e) {}
                }
            }
        }
    
        public void pauseThread() throws InterruptedException {
            pauseThreadFlag = true;
        }
    
        public void resumeThread() {
            synchronized(GUI_INITIALIZATION_MONITOR) {
                pauseThreadFlag = false;
                GUI_INITIALIZATION_MONITOR.notify();
            }
        }
    }

    È una buona idead di utilizzare il monitor come avete fatto. Ma non si può forzare l’attesa da fuori. Bisogna dire che il thread in attesa fino a quando si comunica a lui (oltre il monitor). In questo caso, ti basta questo semplice metodo checkForPaused() che si dovrà mettere in posizioni strategiche in modo non c’è un ritardo fino a quando il thread viene sospeso.

    È anche possibile estendere questa funzione in modo che si può chiedere il thread se lui è in pausa con un flag impostato in checkForPaused() e un public boolean isPausing().

    Perché sincronizzazione su GUI_INITIALIZATION_MONITOR in checkForPaused(), e quindi la sincronizzazione su lock in resumeThread()? Vorrei, inoltre, hanno previsto di utilizzare lo stesso monitor per le chiamate di wait() e notify()
    Ops, è assolutamente giusto. Che era un copia&incolla l’errore che ho fatto. Grazie per l’avviso.

    OriginaleL’autore brimborium

Lascia un commento