【问题标题】:How to restart a stream如何重新启动流
【发布时间】:2020-04-04 17:24:47
【问题描述】:

我有一个表格

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()不工作?

【问题讨论】:

  • 如果你想杀死MainThread并在用户点击JButton时重新启动它,那么调用方法interrupt()是不可行的。你见过这个问题吗? How do you kill a Thread in Java?
  • 不行,对象必须相同,必须运行start()方法。
  • 你是说你被要求在同一个线程对象上多次调用start()
  • 是的,无法更改对象

标签: java multithreading swing


【解决方案1】:

来自the documentation of 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();

请注意,线程主体负责在中断时干净地退出。应始终将中断视为停止您正在执行的操作的请求:

    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 循环放入 try/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();
    }

这将使循环在中断时自动退出。

【讨论】:

  • 据我了解,使用您的解决方案,每次用户单击 JButton 时都会启动一个新线程,但由于方法 @ 中的无限循环,这些线程都不会终止987654328@ 类MainThread。我是否正确地低估了您的解决方案?另外,我认为 OP 想在启动另一个之前终止正在运行的 MainThread。我误解了最初的问题吗?
  • @Abra 你是对的。我将修改答案,指出线程在中断时负责属性自终止。
  • 为了解决我的问题,MainThread对象是一个对我来说很重要。
  • 流被重新启动
【解决方案2】:

因此,为了维护Runnable 的单个实例,您可以在需要时为其安排工作。类似于在单线程 ExecutorService 上调度相同的 Runnable...

你应该把整个 try-catch 块放在while 循环之外,因为中断意味着Thread 的运行时间突然结束,所以Thread 应该采取最后的行动在结束之前放弃/处置其资源。

然后您还可以添加一个boolean 变量来指示Thread 是应该终止还是继续工作。此变量符合while 循环的条件。这里的区别是我们不会中断Thread,而是等到工作的关键部分完成,然后正常退出while循环(之后也会释放资源) )。

基本上用户的体验是中断方式会在中间终止工作,而正常终止需要更多时间,但很优雅。

将这些注释放在一起,您可以得到类似于以下代码的内容:

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 的实例(以及一个运行RunnableThread 实例)。

还要注意doSomeWork 方法的实现可能非常简单,甚至不会抛出InterruptedException,这意味着停止Thread 的唯一正式方法是设置boolean 标志我们正在谈论(在本例中为 on 变量)到 false 并等待最后一项工作(即 doSomeWork 方法)完成。

【讨论】:

    猜你喜欢
    • 2012-10-11
    • 2021-02-21
    • 2010-12-27
    • 2016-12-28
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多