当用户按下退出时,如何中断线程并要求它完成其工作?

时间:2012-10-07 10:11:08

标签: java multithreading

以下代码段是名为“Foo”的线程,它休眠1分钟,然后将 1分钟中键入的数据复制到日志文件中。

         while(isStarted) {
           try {
              Thread.sleep(60000); // sleep for 1 minute
              ArrayList<String> keyStrokeList = nativeMethods.getKeyStrokeList();
              int result = copy.copyToLogFile(keyStrokeList);
              System.out.println(result);
           } catch(Exception exc) {
               exc.printStackTrace();
             }
         }

我将描述一种情况:

Foo 线程已完成复制最后一分钟输入的所有数据,并且自睡眠以来已经过了30秒。这个线程没有意识到当几个键处于睡眠状态时被轻敲的情况,当按下System.exit(0)时,它永远无法将键击复制到日志文件中。

有什么方法可以打断这个线程,即唤醒它并要求它将数据复制到日志文件

请讨论我应该如何解决这个问题。

问题中的情况:

loop started

thread is sleeping and will sleep for 1 minute

after a minute,it gets the keys tapped in the last 1 minute and copies all that
to a file

Thread sleeps again..and will sleep for 1 minute before it copies the keystrokes

It has been about 30 seconds and thread will sleep for 30 seconds more before it starts
copying the key strokes

suddenly the user presses exit button in the application

The user wants that key strokes be recorded till the second he presses exit

I cannot do System.exit(0) before checking the thread is asleep or not

How do I do this. Should I awake it or make a different call to the list and get the 
key strokes because they are being recorded ? And how shall I awake it ?

3 个答案:

答案 0 :(得分:1)

你在那里... ...

while(isStarted) {
    try {
        Thread.sleep(60000); // sleep for 1 minute
    } catch(InterruptedException exc) {
        exc.printStackTrace();
    }
    ArrayList<String> keyStrokeList = nativeMethods.getKeyStrokeList();
    int result = copy.copyToLogFile(keyStrokeList);
    System.out.println(result);
}

您需要提供一种终止循环的方法......

public void dispose() {
    isStarted = false;
    interrupt();
    try {
        join();
    } catch(InterruptedException exc) {
        exc.printStackTrace();
    }
}

您还应该知道,在所有非守护程序线程完成(正常关闭)之前,JVM不会退出。这意味着您可以调用System.exit(0),并且在记录器线程终止之前JVM不会终止。

你可以使用它,但附加一个shut down hook,它有能力在记录器线程上调用dispose方法......只是一个想法

答案 1 :(得分:0)

您应该在2个线程之间使用共享对象来实现等待/通知模式而不是Thread.sleep(..)方法。

在您的情况下,有2个主题:

  • 以1分钟的间隔读取缓冲液。 (线程1)
  • 首先会收到“退出”活动。 (线程2)

因此,每当您创建Thread1的实例时,您都可以将Java Object(new Object())传递给它。读者线程可以使用object.wait(60 * 1000)进入睡眠状态;因此,如果在1分钟内未调用object.notify(),它将睡眠最多1分钟。如果在此持续时间内调用了object.notify(),则线程将立即恢复。

因此,每当用户想要退出应用程序时,您都可以调用object.notify();这将恢复读者线程。

如果由于英语不好而无法向您解释解决方案,请告诉我。我将为您提供代码示例。

答案 2 :(得分:0)

这是一个相当简单的测试用例,展示了一种方法:

public class InterruptTest
{
    @Test
    public void test() throws InterruptedException
    {
        //Create the logging thread and start it
        LogRunnable runnable = new LogRunnable();
        Thread t = new Thread(runnable);
        t.start();

        //Wait some time
        Thread.sleep(3500);

        System.out.println("User has pressed exit, starting shutdown");

        //Tell the runnable to shut down
        runnable.halt();
        //Interrupt the thread to wake it up
        t.interrupt();
        //Wait until thread terminates
        t.join();

        System.out.println("Exiting");
    }

    private static class LogRunnable implements Runnable
    {
        private static final int SLEEPMS = 2000;

        private boolean isStarted = true;
        private int runCount = 1;

        public void halt()
        {
            this.isStarted = false;
        }

        public void run()
        {
            while(isStarted)
            {
                try
                {
                    Thread.sleep(SLEEPMS);
                }
                catch(InterruptedException e)
                {
                    System.out.println("Interrupted");
                }
                catch(Exception exc)
                {
                    exc.printStackTrace();
                }

                //Do work
                System.out.println("Work done " + runCount++);
            }
        }
    }
}

输出:

Work done 1
User has pressed exit, starting shutdown
Interrupted
Work done 2
Exiting
  • 当用户按下退出键时,您发出主线程信号以开始关闭所有内容(在测试用例中,它只是等待一段时间)
  • 通过interrupt() -call
  • 告知记录线程停止并唤醒
  • 在退出之前,主线程调用join()等待日志记录线程完成,您可以考虑使用超时,以防出现问题
  • 日志记录线程以InterruptedException唤醒,在捕获和终止后完成代码
  • 日志记录线程终止后,主线程从join()调用返回并终止