为什么在Java中停止一个线程如此困难?

时间:2011-08-21 15:07:08

标签: java multithreading

我再次遇到其中一种情况,即停止/销毁/暂停线程是不可能的。 .interrupt()没有这个技巧,并且.stop()和.suspend()已被弃用。

很简单的例子:

public class TimerThread extends Thread {

    private JPanel colorPanel;

    public TimerThread(JPanel colorPanel) {
        this.colorPanel = colorPanel;
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
                colorPanel.repaint();
            } catch (Exception ex) {
                //do Nothing
            }
        }
    }
}

这样做是每秒重新绘制一个JPanel来改变它的颜色。我想从另一个类开始和停止这样的线程:

timer = new Thread(new TimerThread(colorPanel));

    startButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            timer.start();
        }
    });

    stopButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            timer.interrupt();
        }
    });

显然(?)这不起作用...我知道我可以使用Timer,SwingWorker或将计时器声明为timer = new TimerThread(colorPanel);并在run方法中使用布尔值而不是“true”,但是我被要求将计时器声明为“线程”而没有别的。

令我惊讶的是(或者这是愚蠢的?),即使这不起作用:

startButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            timer = new Thread(new TimerThread(colorPanel));
            timer.start();
        }
    });

    stopButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            timer.interrupt();
            timer = null;
        }
    });

所以我的问题很简单:如何在Java中创建线程启动/暂停/恢复/停止?

7 个答案:

答案 0 :(得分:5)

当你得到一个中断时,你应该开始清理并返回a.s.a.p. (或者至少重置中断的状态)

while (true) {
    try {
        Thread.sleep(1000);
        colorPanel.repaint();
    } catch(InterruptedException e){//from sleep
        return;//i.e. stop
    } catch (Exception ex) {
        //do Nothing
    }
}

另一种方法是在条件中检查Thread.interrupted()(但是你需要在InterruptedException的catch中重置被中断的状态

然而,在摇摆中,您可以使用javax.swing.Timer让事件每隔一段时间运行一次,然后使用api

停止
javax.swing.Timer timer = new Timer(1000,new ActionListener() {
     public void actionPerformed(ActionEvent e) {
         colorPanel.repaint();
     }
});
timer.setRepeats(true);

startButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        timer.start();
    }
});

stopButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        timer.stop();
    }
});

答案 1 :(得分:3)

试试这个:

public class TimerThread extends Thread {

    private volatile boolean stop = false;
    private JPanel colorPanel;

    public TimerThread(JPanel colorPanel) {
        this.colorPanel = colorPanel;
    }

    public void stopTimer() {
        stop = true;
    }

    public void run() {
        while (stop == false) {
            try {
                Thread.sleep(1000);
                colorPanel.repaint();
            } catch (Exception ex) {
                //do Nothing
            }
        }
    }
}

// Why new Thread(new TimerThread(...))?
// timer = new Thread(new TimerThread(colorPanel)); 

timer = new TimerThread(colorPanel)

startButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        timer.start();
    }
});

stopButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        timer.stopTimer();
    }
});

另请查看here,了解如何在不推荐使用stop的情况下复制{{1}}。

答案 2 :(得分:1)

基本上,你让他们合作。你有一些共享的标志让他们看到他们应该做什么,以及当你 睡觉时,而不是你在某个共享监视器上wait。然后,当你想控制线程时,你设置适当的标志和notify监视器,这样如果线程正在等待,它将被唤醒并注意它应该暂停/停止/无论如何。显然,你需要对共享状态采取正常的关注,使用volatile变量,Atomic *对象或锁定来确保每个线程都能看到每个其他线程所做的更新。

任何 - 合作都是有风险的,因为在手术中途可能会破坏状态。

答案 3 :(得分:1)

先发制人地阻止线程是危险的。这样做会导致死锁,资源泄漏等。相反,你应该使用合作信号机制。

向您希望它停止的线程发出信号,然后等待它执行此操作。线程应定期检查是否需要停止并做出相应的反应。

答案 4 :(得分:1)

不应循环while (true),而应该在线程未被中断时循环:

@Override public void void() {
  // some kind of initialization...
  while (!Thread.currentThread().isInterrupted()) {
    try { ...
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt(); // ensure interrupt flag is set
    }
  }
  // some kind of cleanup
}

如果InterruptedException块内的任何内容都没有抛出while,你要么不使用阻塞操作(在这个线程上简单地调用Thread.interrupt()会在下一次迭代时停止它)或者你使用一些表现不佳的阻塞调用(JCL本身有很多这样的例子!)。

答案 5 :(得分:0)

执行此操作的正确方法确实是拥有一个变量来确定何时应该停止Thread,退出其run方法。您可以找到有关如何正确执行此操作的详细信息here

答案 6 :(得分:0)

使用此解决方案,您不会获得等待/通知或中断可能获得的“即时”更新,但如果您不介意第二次延迟的一小部分,则应该完成此任务。

volatile boolean stopped = false;
volatile boolean paused = false;

pauseButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        paused = true;
    }
});

resumeButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        paused = false;
    }
});

stopButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        stopped = true;
    }
});

... TimerThread

public void run() {
    while (stopped == false) {
        try {
            Thread.sleep(1000);
            if (stopped)
               break;
            if (!paused) 
               colorPanel.repaint();
        } catch (Exception ex) {
            //do Nothing
        }
    }
}