简体   繁体   English

java中的取消和中断

[英]Cancellation and Interruption in java

In Java Concurrency in Practice there is explanation about how to use cancellation and interruption in threads.Java Concurrency in Practice 中有关于如何在线程中使用取消和中断的说明。 This example is on Page 21 of Chapter 7 Cancellation and Shutdown, which states:此示例位于第 7 章取消和关闭的第 21 页,其中指出:

Listing 7.3.清单 7.3。 Unreliable Cancellation that can Leave Producers Stuck in a Blocking Operation.不可靠的取消会使生产者陷入阻塞操作。 Don't Do this.不要这样做。

Here they are telling us in order to stop any thread operation just create a volatile flag which can be checked.他们在这里告诉我们,为了停止任何线程操作,只需创建一个可以检查的易失性标志。 Depending on the status of that flag thread execution stops.根据该标志线程执行停止的状态。

Now there is one program for explaining same.现在有一个程序可以解释相同的内容。 It works fine there, below is the example:它在那里工作正常,以下是示例:

public class PrimeGenerator implements Runnable {
    @GuardedBy("this")
    private final List<BigInteger> primes = new ArrayList<BigInteger>();
    private volatile boolean cancelled;

    public void run() {
        BigInteger p = BigInteger.ONE;
        while (!cancelled) {
            p = p.nextProbablePrime();
            synchronized (this) {
                primes.add(p);
            }
        }
    }

    public void cancel() {
        cancelled = true;
    }

    public synchronized List<BigInteger> get() {
        return new ArrayList<BigInteger>(primes);
    }

    List<BigInteger> aSecondOfPrimes() throws InterruptedException {
        PrimeGenerator generator = new PrimeGenerator();
        new Thread(generator).start();
        try {
            SECONDS.sleep(1);
        } finally {
            generator.cancel();
        }
        return generator.get();
    }
}

In the above code cancelled is the volatile flag which we can check for the cancellation check and thread execution stops if its true.在上面的代码中, cancelled是 volatile 标志,我们可以检查取消检查,如果为真,线程执行将停止。

But if we do the same operation which we have done above but use BlockingQueue there is some problem.但是,如果我们执行上面所做的相同操作,但使用BlockingQueue则存在一些问题。

If, however, a task that uses this approach calls a blocking method such as BlockingQueue.put() we could have a more serious problem the task might never check the cancellation flag and therefore might never terminate.但是,如果使用这种方法的任务调用阻塞方法(例如BlockingQueue.put()我们可能会遇到更严重的问题,该任务可能永远不会检查取消标志,因此可能永远不会终止。

BrokenPrimeProducer in below program illustrates this problem.下面程序中的BrokenPrimeProducer说明了这个问题。 The producer thread generates primes and places them on a blocking queue.生产者线程生成素数并将它们放在阻塞队列中。 If the producer gets ahead of the consumer, the queue will fill up and put() will block.如果生产者领先于消费者,队列将填满并且put()将阻塞。 What happens if the consumer tries to cancel the producer task while it is blocked in put() ?如果在put()被阻塞时消费者试图取消生产者任务会发生什么? It can call cancel which will set the cancelled flag but the producer will never check the flag because it will never emerge from the blocking put() (because the consumer has stopped retrieving primes from the queue).它可以调用取消设置cancelled标志,但生产者永远不会检查该标志,因为它永远不会从阻塞put() (因为消费者已停止从队列中检索素数)。

Here is the code for the same:这是相同的代码:

class BrokenPrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;
    private volatile boolean cancelled = false;

    BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!cancelled) {
                queue.put(p = p.nextProbablePrime());
            }
        } catch (InterruptedException consumed) {
        }
    }

    public void cancel() {
        cancelled = true;
    }


    void consumePrimes() throws InterruptedException {
        BlockingQueue<BigInteger> primes =...;
        BrokenPrimeProducer producer = new BrokenPrimeProducer(primes);
        producer.start();

        try {
            while (needMorePrimes()) {
                consume(primes.take());
            }
        } finally {
            producer.cancel();
        }
    }
}

I am not able to understand why cancellation will not work in case of blocking Queue in second code example.我无法理解为什么在第二个代码示例中阻塞 Queue 的情况下取消将不起作用。 Can someone explain?有人可以解释一下吗?

This is explicitly because BlockingQueue#put(E) will block if it needs to while placing values inside of it. 这是明确的,因为BlockingQueue#put(E)如果需要在放入值的同时会阻塞 The code isn't in a position to check the flag again due to it being in a blocked state, so the fact that the flag is set to a different value at any other time is independent of the currently blocked thread. 该代码由于处于阻塞状态而无法再次检查该标志,因此该标志在任何其他时间都设置为其他值的事实与当前被阻塞的线程无关。

The only real way to address the issue is to interrupt the thread, which will end the blocking operation. 解决该问题的唯一真实方法是中断线程,这将结束阻塞操作。

When using a flag to cancel, there's no way to make the thread quit sleeping or waiting if it happens to have started sleeping or waiting, instead you have to wait for the sleep time to expire or for the wait to be ended with a notification. 使用标志取消时,如果线程恰好开始休眠或等待,则无法使线程退出休眠或等待,而是必须等待休眠时间到期或等待以通知结束。 Blocking means a consumer thread sits in a wait state until something gets enqueued in an empty queue, or a producer thread sits in a wait state until there's room to put something in a full queue. 阻塞意味着使用者线程处于等待状态,直到某些内容被排入空队列,或者生产者线程处于等待状态,直到有空间将某些内容放入完整队列。 The blocked thread never leaves the wait method -- it's as if you had a breakpoint on the line with the sleep or wait, and the thread is frozenon that line until the sleep time expires or until the thread gets a notification (not getting into spurious wakeups). 被阻塞的线程永远不会离开wait方法-就像您在睡眠或等待的行上有一个断点一样,并且线程在该行上被冻结,直到睡眠时间到期或直到线程收到通知为止(不会陷入虚假状态)唤醒)。 The thread can't get to the line where it checks the flag. 线程无法到达检查标志的行。

Using interruption signals the thread to wake up if it is waiting or sleeping. 使用中断会通知线程在等待或睡眠时唤醒。 You can't do that with a flag. 您不能使用标志来做到这一点。

Cancellation flags need to be checked. 取消标志需要检查。 Whereas interruption immediately notifies the thread blocked to throw InterruptedException , only the next iteration of the while loop will the thread know it's been changed - that is, when the thread unblocks and continues . 中断立即通知被阻塞的线程抛出InterruptedException ,而仅while循环的下一次迭代将使线程知道它已被更改-也就是说,当线程解除阻塞并继续时

See the problem? 看到问题了吗? The thread won't know if another thread set the flag. 该线程将不知道另一个线程是否设置了该标志。 It's blocked. 被封锁了 It can't go to the next iteration. 它不能转到下一个迭代。

needMorePrimes()在某些条件下返回false ,然后消费者将调用producer.cancel() ,同时生产者将 BlockingQueue 填满,使其阻塞在queue.put(p = p.nextProbablePrime())并且可以不检查取消状态,所以很糟糕。

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

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