Как перезапустить поток - PullRequest
0 голосов
/ 04 апреля 2020

У меня есть форма

public class MainFrame extends JFrame {
    private int colThread=0;
    MainThread mt=new MainThread("Поток - 1");

    public MainFrame()  
    {
        setSize(300,300);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel jp = new JPanel();
        JButton jb=new JButton("Запустить поток");
        jb.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                jb.setText("Перезапустить поток");
                colThread = colThread + 1;
                if (!mt.isInterrupted())
                {
                    mt.interrupt();
                }
                mt.start();             
            }           
        });
        jp.add(jb);
        add(jp);
    }
}

У меня есть класс потока:

public class MainThread extends Thread{
    private int summ;
    private String threadName;

    public MainThread(String threadName)
    {
        this.threadName = threadName;
    }   

    @Override
    public void run() {
        summ = 0;
        while(true)
        {
            summ = summ +1;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(this.threadName + " " + summ);
        }
    }
}

У меня есть основной класс:

public class MainClass {
    public static void main(String[] args) {
        MainFrame mf = new MainFrame();
        mf.setVisible(true);
    }
}

Вопрос в том, как возобновить выполнение потока при нажатии кнопки. При выполнении программы в этой форме возникает ошибка, но это понятно, потому что поток работает, не ясно, почему не работает interrupt ()?

Ответы [ 2 ]

1 голос
/ 04 апреля 2020

С документация Thread.start () :

Никогда не разрешается запускать поток более одного раза. В частности, поток не может быть перезапущен после завершения выполнения.

Но вы можете создать одну реализацию Runnable и передать ее конструктору Thread несколько раз:

public class MainThread implements Runnable {

А затем в своем классе MainFrame сделайте следующее:

public class MainFrame extends JFrame {
    private int colThread=0;
    private MainThread task = new MainThread("Поток - 1");
    private Thread mt = null;

    // ...

        public void actionPerformed(ActionEvent arg0) {
            jb.setText("Перезапустить поток");
            colThread = colThread + 1;
            if (mt != null && !mt.isInterrupted())
            {
                mt.interrupt();
            }

            mt = new Thread(task);
            mt.start();

Обратите внимание, что тело Thread отвечает за чистый выход при прерывании. Прерывание всегда следует рассматривать как запрос на прекращение того, что вы делаете:

    while(true)
    {
        summ = summ +1;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            break;      // Someone wants this method to exit!
        }
        System.out.println(this.threadName + " " + summ);
    }

Еще лучший подход - поместить while l oop в попытку / catch:

    try {
        while(true)
        {
            summ = summ +1;
            Thread.sleep(1000);
            System.out.println(this.threadName + " " + summ);
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Это заставит l oop автоматически выйти из режима прерывания.

0 голосов
/ 05 апреля 2020

Итак, чтобы поддерживать один экземпляр Runnable, вы можете запланировать его работу на него, когда это необходимо. Что-то вроде планирования одного и того же Runnable в одном потоке ExecutorService ...

Вы должны поместить весь блок try-catch вне while l oop, поскольку прерывание означает внезапное завершение времени выполнения Thread, поэтому Thread должен предпринять свои последние действия для освобождения / распоряжения своими ресурсами перед завершением.

Затем вы также можете добавить переменную boolean указывает, должен ли Thread завершаться или продолжать работать. Эта переменная соответствует условию while l oop. Разница здесь в том, что мы не прерываем Thread, но вместо этого мы ждем, пока критическая часть работы не будет завершена, и затем нормально завершаем работу while l oop (также распределяя ресурсы после этого).

По существу, пользовательский опыт может заключаться в том, что подход прерывания убивает работу в середине, тогда как обычное завершение занимает немного больше времени, но изящно.

Помещение вместе с этими заметками вы можете получить что-то вроде следующего кода:

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main2 {

    public static class DisposableRunnable implements Runnable {
        private boolean on = true;

        @Override
        public void run() {
            try {
                while (isOn())
                    doSomeWork();
                System.out.println(Thread.currentThread().getName() + " is stopped gracefully.");
            }
            catch (final InterruptedException ix) {
                System.out.println(Thread.currentThread().getName() + " is stopped abruptly: " + ix);
            }
            finally {
                dispose();
            }
        }

        //Do whatever work the thread has to do, in this method:
        private void doSomeWork() throws InterruptedException {
            for (int i = 0; i < 5; ++i) {
                System.out.println("Working " + i + "...");
                Thread.sleep(500); //Delay a bit, to simulate a lengthy work in progress.
            }
        }

        private void dispose() {
            //Relinquish resources here...
        }

        public synchronized boolean isOn() {
            return on;
        }

        public synchronized void stop() {
            on = false;
        }
    }

    public static class MainFrame extends JFrame {
        private final Thread mt;
        private final DisposableRunnable run;

        public MainFrame() {
            run = new DisposableRunnable();
            mt = new Thread(run);
            final JPanel jp = new JPanel();
            final JButton jb1 = new JButton("Click to stop.");
            final JButton jb2 = new JButton("Click to interrupt.");
            jb1.addActionListener(e -> {
                run.stop();
                jb1.setText("Stopped.");
                jb1.setEnabled(false);
                jb2.setEnabled(false);
            });
            jb2.addActionListener(e -> {;
                mt.interrupt();
                jb2.setText("Interrupted");
                jb1.setEnabled(false);
                jb2.setEnabled(false);
            });
            jp.add(jb1);
            jp.add(jb2);
            super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            super.getContentPane().add(jp);
            super.pack();
            super.setLocationRelativeTo(null);
        }

        public void startOnce() {
            mt.start();
            setVisible(true);
        }
    }

    public static void main(final String[] args) {
        new MainFrame().startOnce();
    }
}

Разница в том, что мы делаем только один экземпляр Runnable (а также один экземпляр Thread, который запускает Runnable).

Обратите также внимание, что реализация метода doSomeWork может быть настолько простой, что он даже не выдает InterruptedException, что означает, что единственным формальным способом остановки Thread будет установите флаг boolean, о котором мы говорили (в данном случае переменная on), на false, а ожидание последнего фрагмента работы (ie метод doSomeWork) на значение Fini * 104. 4 *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...