简体   繁体   English

如何从另一个线程中断一个线程中的循环?

[英]How do I break a loop in one Thread from another Thread?

I am new to Java and I am trying to use one Thread to finish a loop in another Thread regardless of the state of the loop. 我是Java的新手,我正在尝试使用一个Thread来完成另一个Thread中的循环,而不管循环的状态如何。

public static void main(String[] args) {
    // Either start the other thread here
        while(true){
            // Or here, not quite sure
            // Do stuff
        }
    }
}



public class Timer implements Runnable{

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        while(true) {
            long current = System.currentTimeMillis();
            if(current - start == 10000){
                // How do I notify the loop in main to break?
                break;
            }
        }
    }
}

What I am trying to do is end the loop in main after 10 seconds, regardless of the state of its loop because the loop contains a Scanner reading from System.in , and it needs to stop after 10 seconds regardless of what has been read from the keyboard or not. 我想要做的是在10秒后结束主循环,无论其循环状态如何,因为循环包含从System.in读取的Scanner ,并且它需要在10秒后停止,无论从何处读取键盘与否。 I thought the best solution would be to have a timer Thread running which counts the seconds, and then notify the loop in the other Thread somehow, after 10 seconds, to break, however, I do not know how to implement this... 我认为最好的解决方案是让计时器Thread运行,计算秒数,然后在10秒后以某种方式通知另一个Thread中的循环,但是,我不知道如何实现这个...

If your goal is to stop looping after 10 seconds, there's no reason to use another thread. 如果您的目标是在10秒后停止循环,则没有理由使用其他线程。 Just check the time locally: 只需在当地查看时间:

public class Main {
    public static void main(String[] args) {
        // Instructions

        long start = System.currentTimeMillis();
        do {
            //Instructions
            //Instructions
            //...
            //Instructions   
        } while (System.currentTimeMillis() - start < 10000);
    }
}

How about something like this: 这样的事情怎么样:

public static void main(String[] args) {
    // Instructions
    AtomicBoolean shouldStop = new AtomicBoolean(false);

   Timer timer = new Timer(shouldStop);
   // start the timer thread
        while(true){
            if (shouldStop.get()) {
               break;
            }

            //Instructions
        }
    }
}



public class Timer implements Runnable{
   private final AtomicBoolean shouldStop;

   public Timer(AtomicBoolean shouldStop) {
        this.shouldStop = shouldStop;
   }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        while(true){
            long current = System.currentTimeMillis();
            if(current - start == 10000) {
                shouldStop.set(true);
                break;
            }
        }
    }
}

The safest approach to your problem is to use a volatile variable: 解决问题最安全的方法是使用volatile变量:

public class Main {

    private static volatile boolean keepRunning = false;

    public static void main(String[] args) {
        keepRunning = true;

        while(keepRunning) {
            //Instructions
            //...
            //Instructions   
        }
    }
}

public class Timer implements Runnable {

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        while(true){
            long current = System.currentTimeMillis();
            if(current - start == 10000){
                // Notify the loop in Main to break
                keepRunning = false;
                break;
            }
        }
    }

}

Don't reinvent the wheel. 不要重新发明轮子。 Use an ExecutorService and a get() with a timeout: 使用ExecutorService和带超时的get()

ExecutorService executor = Executors.newSingleThreadExecutor();

// Here's a lambda, but you could use an instance of a normal Runnable class
Runnable runnable = () -> {
    while(true){
        // Do stuff
    }
};

Future<?> future = executor.submit(runnable);

try {
    future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    // the task timed out
    future.cancel(true); // this will kill the running thread
} catch (InterruptedException | ExecutionException e) {
    // the runnable exploded
}

There are many solutions to this. 有很多解决方案。 Here is one of them: 这是其中之一:

public class Main {

    public static void main(String... args) {
        Thread thread = () -> {
            while(true) {
                // We call Thread.interrupted to check the interrupted status of the Thread.
                // This method also clears the interrupted status of the Thread.
                if(Thread.interrupted()) {
                    break;
                }
                // code...
            }
        }

        Thread timer = () -> {
            long start = System.currentTimeMillis();
            while(true) {
                long current = System.currentTimeMillis();
                if(current - start == 10_000){ // underscore for clarity
                    // This causes the thread to interrupt. The next pass
                    // in our loop in "thread" will first check its interrupted status
                    // before continuing, and will break if the status is interrupted
                    thread.interrupt();
                    break;
                }
            }
        }

        // wrap in synchronized block to ensure both threads run simultaneously
        synchronized(Main.class) {
            thread.start();
            timer.start();
        }
    }
}

Explanation 说明

In this solution, we use interrupts to make the while-loop in thread break. 在这个解决方案中,我们使用中断thread 中断中进行while循环。 An interrupt stops normal thread operation at whatever point it is executing, but only if the thread calls a method that throws InterruptedException , and the thread returns (or breaks) in the catch block. 中断在正在执行的任何时刻停止正常的线程操作,但前提是线程调用抛出InterruptedException的方法,并且线程在catch块中返回(或中断)。 Thread.interrupt() sets the interrupt status to interrupted , but does not actually interrupt the Thread from execution. Thread.interrupt()中断状态设置中断 ,但实际上不会中断线程执行。 The run method of thread is invoked again after being interrupted. threadrun方法在被中断后再次被调用。 Since we check Thread.interrupted() at the beginning of our loop, the loop will break when the loop is entered, and run exits and the Thread ceases to run. 因为我们在循环开始时检查Thread.interrupted() ,所以在循环进入时循环将中断,并且run退出并且Thread停止运行。 With Thread.interrupted() , you have to check, in the same Thread, for its interrupted status, and then choose what to do if the status is cleared or not. 使用Thread.interrupted() ,您必须在同一个线程中检查其中断状态,然后选择清除状态时要执行的操作。

As a side note, if our only branch (if-statement) in the loop checks for the interrupted state of that thread, then it might be easier to read and be more efficient if we simply declare our while-loop as while(!Thread.interrupted()) . 作为旁注,如果循环中我们唯一的分支(if语句)检查该线程的中断状态,那么如果我们简单地将while循环声明为while(!Thread.interrupted()) ,那么它可能更容易阅读并且效率更高while(!Thread.interrupted()) Otherwise it should be left as it is. 否则它应保持原样。

Other Solutions 其他方案

Other solutions are illustrated by some other answers here, though some answer your whole problem in a different way . 其他解决方案在这里通过其他一些答案来说明,尽管有些解决方案以不同的方式回答您的问题 Some other solutions include using an AtomicBoolean as answered by Oliver Dain; 其他一些解决方案包括使用Oliver Dain回答的AtomicBoolean; another using a global lock object and volatile booleans or AtomicBoolean. 另一个使用全局锁定对象和volatile布尔值或AtomicBoolean。

There are many solutions, but the interrupt solution seems to be the easiest and most convenient in most use-cases. 有许多解决方案,但在大多数用例中,中断解决方案似乎是最简单和最方便的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM