英文:
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
>不允许启动一个线程超过一次。特别地,一旦线程完成执行,就不能重新启动。
但是您可以创建一个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() + " 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();
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论