【问题标题】:Java Thread Start-Stop-Start on same button click in EclipseJava Thread Start-Stop-Start on the same button click in Eclipse
【发布时间】:2014-09-24 16:14:00
【问题描述】:

我正在创建一个简单的 java 程序,其 GUI 是在 Eclipse 中的窗口构建器的帮助下构建的。 GUI 只包含一个按钮。

我的目标:- 单击按钮时,启动一个线程,该线程将无限打印到控制台编号,直到再次单击同一按钮将其停止。

这是我的实现:-

Runner.java(线程类)

public class Runner extends Thread{

    private volatile boolean running = true;
    private int i = 1;

    @Override
    public void run() {

        while(running)
        {
            System.out.println(i++ +"\n");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }



    public void shutdown()
    {
        running = false;
    }

}

MainGUI.java(UI类)

Runner runIT = new Runner();
final JButton btnNewButton = new JButton("Start Thread");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {

                String buttonText = btnNewButton.getText();
                if(buttonText.equals("Start Thread"))
                {
                    btnNewButton.setText("Stop Thread");
                    runIT.start();
                }

                else if(buttonText.equals("Stop Thread"))
                {
                    btnNewButton.setText("Start Thread");
                    runIT.shutdown();

                }
            }
        });

我的问题:- 线程完美地启动和停止,但只有一次。也就是说,我只能启动和停止线程一次。当我尝试重新启动停止的线程时,它失败了。 我希望线程继续从它停止的相同值打印数字。

Error :- Exception in thread "AWT-EventQueue-0" java.lang.IllegalThreadStateException
    at java.lang.Thread.start(Thread.java:705)
    at com.zakoi.java.thread.GUI.MainGUI$2.actionPerformed(MainGUI.java:62)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
    at java.awt.Component.processMouseEvent(Component.java:6516)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3311)
    at java.awt.Component.processEvent(Component.java:6281)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4872)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422)
    at java.awt.Container.dispatchEventImpl(Container.java:2273)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4698)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:735)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:694)
    at java.awt.EventQueue$3.run(EventQueue.java:692)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:708)
    at java.awt.EventQueue$4.run(EventQueue.java:706)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:705)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

请帮忙。

【问题讨论】:

标签: java eclipse multithreading


【解决方案1】:

您应该让线程运行但充当服务。然后应该通过与它的某种形式的通信来控制它。

创建线程非常昂贵 - 不应不必要地创建/销毁它们。

此代码使用锁来实现可以暂停和恢复的Thread。这里的内容比您要寻找的要多,但它证明了理论。

/**
 * PauseableThread is a Thread with pause/resume and cancel methods.
 *
 * The meat of the process must implement `step`.
 *
 * You can either extend this and implement `step` or use the factory.
 *
 * I cannot extend Thread because my resume will clash.
 *
 */
public abstract class PauseableThread implements Runnable {

    // The lock.

    private final ReadWriteLock pause = new ReentrantReadWriteLock();
    private final Lock readLock = pause.readLock();
    private final Lock writeLock = pause.writeLock();
    // Flag to cancel the wholeprocess.
    private volatile boolean cancelled = false;
    // The exception that cause it to finish.
    private Exception thrown = null;
    // The thread that is me.
    private Thread me = null;

    @Override
    // The core run mechanism.
    public void run() {
        // Track my current thread.
        me = Thread.currentThread();
        try {
            while (!finished()) {
                // Block here if we're paused.
                blockIfPaused();
                // Don't do any more work if we've been asked to stop.
                if (!finished()) {
                    // Do my work.
                    step();
                }
            }
        } catch (Exception ex) {
            // Just fall out when exception is thrown.
            thrown = ex;
        }
    }

    // Have we finished yet?
    private boolean finished() {
        return cancelled || !me.isInterrupted();
    }

    // Block if pause has been called without a matching resume.
    private void blockIfPaused() throws InterruptedException {
        try {
            // Grab a write lock. Will block if a read lock has been taken.
            writeLock.lockInterruptibly();
        } finally {
            // Release the lock immediately to avoid blocking when pause is called.
            writeLock.unlock();
        }
    }

    // Pause the work. NB: MUST be balanced by a resume.
    public void pause() {
        // We can wait for a lock here.
        readLock.lock();
    }

    // Resume the work. NB: MUST be balanced by a pause.
    public void resume() {
        // Release the lock.
        readLock.unlock();
    }

    // Stop.
    public void cancel() {
        // Stop everything.
        cancelled = true;
    }

    // Stop immediately (if param is true).
    public void cancel(boolean interrupt) {
        if (interrupt) {
            // Interrupt me.
            me.interrupt();
        } else {
            // Or cancel me.
            cancel();
        }
    }

    // Wait for completion.
    public void await() throws InterruptedException {
        // Wait 'till we've finished. NB: Will wait forever if you haven't instigated a cancel of some kind.
        while (me.isAlive()) {
            Thread.sleep(0);
        }
    }

    // Start - like a thread.
    public void start() {
        // Wrap me in a thread and fire the sucker up!
        new Thread(this).start();
    }

    // Get the exception that was thrown to stop the thread or null if the thread was cancelled.
    public Exception getThrown() {
        return thrown;
    }

    // Expose my Thread.
    public Thread getThread() {
        return me;
    }

      // Create this method to do stuff.
    // Calls to this method will stop when pause is called.
    // Any thrown exception stops the whole process.
    public abstract void step() throws Exception;

    // Factory to wrap a Stepper in a PauseableThread
    public static PauseableThread make(Stepper stepper) {
        StepperThread pauseableStepper = new StepperThread(stepper);
        // That's the thread they can pause/resume.
        return pauseableStepper;
    }

    // One of these must be used.
    public interface Stepper {

        // A Stepper has a step method.
        // Any exception thrown causes the enclosing thread to stop.

        public void step() throws Exception;
    }

    // Holder for a Stepper.
    private static class StepperThread extends PauseableThread {

        // The actual stepper I am proxying.

        private final Stepper stepper;

        StepperThread(Stepper stepper) {
            this.stepper = stepper;
        }

        @Override
        public void step() throws Exception {
            stepper.step();
        }
    }

      // !!!! Testing only below !!!!
    // My test counter.
    static int n = 0;

    // Test/demo.
    public static void main(String[] args) throws InterruptedException {

        try {
            // Simple stepper that just increments n.
            Stepper s = () -> {
                n += 1;
                Thread.sleep(1);
            };
            PauseableThread pt = PauseableThread.make(s);
            // Start it up.
            pt.start();
            Thread.sleep(1000);
            pt.pause();
            System.out.println("Paused: " + n);
            Thread.sleep(1000);
            System.out.println("Resuminng: " + n);
            pt.resume();
            Thread.sleep(1000);
            pt.cancel();
            System.out.println("Finished: " + n);

            // Start again to test agressive cancelling.
            pt.await();
            n = 0;
            pt = PauseableThread.make(s);
            // Start it up.
            pt.start();
            Thread.sleep(1000);
            pt.pause();
            System.out.println("Paused: " + n);
            Thread.sleep(1000);
            System.out.println("Resuminng: " + n);
            pt.resume();
            Thread.sleep(1000);
            // Cancel aggressively.
            pt.cancel(true);
            System.out.println("Finished: " + n);
            System.out.println("thrown: " + pt.getThrown());

        } catch (InterruptedException e) {
        }
    }
}

【讨论】:

    【解决方案2】:

    多次启动一个线程是不合法的。特别是,线程一旦完成执行就可能不会重新启动。

    --来自Thread API

    【讨论】:

    • :- 假设我想继续从线程停止的相同号码打印.. 我该怎么做呢?
    • 您将需要一种 线程安全 方法来在您的 Runner 类关闭后检索一个值(volatile int 是一种可能性,但不一定是最好的) 和 thread-safe 表示在启动之前初始化下一个 new Runner 中的值。
    【解决方案3】:

    首先,永远不要手写 GUI 代码,有很多很棒的工具可以生成完美组织的模块化和可读代码。我个人最喜欢的是 Swing : Netbeans GUI builder,如果它的 JavaFX(与 Swing 相比很棒),那么它的 SceneBuilder 又是由 Oracle 官方支持和提供的。

    第二个也是最重要的线程只能启动一次...所以请确保您的代码不会尝试再次启动它...这就是为什么您会收到 IllegalStateException 因为线程实例执行已经完成...

    线程也是重对象,创建这些对象时应该小心,在您的情况下当然可以,但通常拥有线程池和重用线程是件好事...始终避免手动创建线程,因为您必须跟踪它们正确完成,如果您让任何非守护线程处于活动状态,您的应用程序将无法完成其执行...

    【讨论】:

      【解决方案4】:

      一个简单解决方案的草图:

      • 你的类不需要扩展Thread,只需实现Runnable
      • 在监听按钮事件时,首先确保您只对那些您真正想要的事件做出反应
      • 在你的ActionListener中,每次你想启动一个新的线程,用你的Runnable对象初始化它并调用Thread.start()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多