重新启动流程

huangapple go评论88阅读模式
英文:

How to restart a stream

问题

我有一个表单

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

英文:

I have a form

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);
    }
}

I have a thread class:

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);
        }
    }
}

I have the main class:

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

The question is how to restart the thread execution when the button is clicked. When executing the program in this form, an error occurs, but this is understandable, because the thread works, it is not clear why interrupt () does not work?

答案1

得分: 1

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;      // 有人希望此方法退出!
    }
    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();
}

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

英文:

From the documentation of Thread.start():

>It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

But you can create a single implementation of Runnable, and pass that to the Thread constructor repeatedly:

public class MainThread implements Runnable {

And then in your MainFrame class, do this:

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();

Note that a Thread body is responsible for exiting cleanly when interrupted. An interrupt should always be treated as a request to stop what you’re doing:

    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);
    }

An even better approach is to put your while loop inside the 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();
    }

This will make the loop exit automatically when interrupted.

答案2

得分: 0

所以,为了保持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 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的一个实例(以及运行Runnable的一个Thread实例)。

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

英文:

So, in order to maintain a single instance of the Runnable, you can schedule its work to it when needed. Something like scheduling the same Runnable at a single-thread ExecutorService...

You should put the whole try-catch block outside the while loop, because interruption means end of the Thread's runtime abruptly, so the Thread should take its last actions to relinquish/dispose its resources, before ending.

Then you can also add a boolean variable indicating whether the Thread should terminate or keep on working. This variable goes at the while loop's condition. The difference here is that we do not interrupt the Thread, but instead we wait until a critical portion of the work is finished, and then quit the while loop normally (also disposing resources after that).

Basically the user's experience would be that the interruption approach kills the work in the middle of it, while the normal termination takes a bit more time, but is graceful.

Putting those notes together you could get something like the following code:

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() + &quot; is stopped gracefully.&quot;);
}
catch (final InterruptedException ix) {
System.out.println(Thread.currentThread().getName() + &quot; is stopped abruptly: &quot; + ix);
}
finally {
dispose();
}
}
//Do whatever work the thread has to do, in this method:
private void doSomeWork() throws InterruptedException {
for (int i = 0; i &lt; 5; ++i) {
System.out.println(&quot;Working &quot; + i + &quot;...&quot;);
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(&quot;Click to stop.&quot;);
final JButton jb2 = new JButton(&quot;Click to interrupt.&quot;);
jb1.addActionListener(e -&gt; {
run.stop();
jb1.setText(&quot;Stopped.&quot;);
jb1.setEnabled(false);
jb2.setEnabled(false);
});
jb2.addActionListener(e -&gt; {;
mt.interrupt();
jb2.setText(&quot;Interrupted&quot;);
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();
}
}

The difference here is that we only make one instance of the Runnable (and also one instance of the Thread which runs the Runnable).

Note also that the doSomeWork method's implementation could be so simple that it doesn't even throw an InterruptedException, which means the only formal way of stopping the Thread would be to set the boolean flag we were talking about (in this case the on variable) to false and waiting for the last piece of work (ie the doSomeWork method) to finish.

huangapple
  • 本文由 发表于 2020年4月5日 01:24:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/61031973.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定