1. Introduction
1.绪论
In this tutorial, we’ll explore Java’s InterruptedException. First, we’ll quickly go through the life cycle of a thread with an illustration. Next, we’ll see how working in multithreaded applications can potentially cause an InterruptedException. Finally, we will see how to handle this exception.
在本教程中,我们将探讨Java的InterruptedException。首先,我们将通过一个插图快速了解一个线程的生命周期。接下来,我们将看到在多线程应用程序中工作如何有可能导致InterruptedException。最后,我们将看到如何处理这个异常。
2. Multithreading Basics
2.多线程基础知识
Before discussing interrupts, let’s review multithreading. Multithreading is a process of executing two or more threads simultaneously. A Java application starts with a single thread – called the main thread – associated with the main() method. This main thread can then start other threads.
在讨论中断之前,让我们回顾一下多线程。多线程是一个同时执行两个或多个线程的过程。一个Java应用程序开始时有一个单线程–称为主线程–与main()方法相关联。然后这个主线程可以启动其他线程。
Threads are lightweight, which means that they run in the same memory space. Hence, they can easily communicate among themselves. Let’s take a look at the life cycle of a thread:
线程是轻量级的,这意味着它们在同一内存空间中运行。因此,它们可以很容易地在彼此之间进行通信。让我们来看看线程的生命周期。
As soon as we create a new thread, it’s in the NEW state. It remains in this state until the program starts the thread using the start() method.
一旦我们创建一个新的线程,它就处于NEW状态。在程序使用start()方法启动该线程之前,它一直处于这种状态。
Calling the start() method on a thread puts it in the RUNNABLE state. Threads in this state are either running or ready to run.
在一个线程上调用start()方法会使它处于RUNNABLE状态。处于这种状态的线程要么正在运行,要么准备运行。
When a thread is waiting for a monitor lock and is trying to access code that is locked by some other thread, it enters the BLOCKED state.
当一个线程在等待监控锁,并试图访问被其他线程锁定的代码时,它就进入了BLOCKED状态。
A thread can be put in the WAITING state by various events, such as a call to the wait() method. In this state, a thread is waiting for a signal from another thread.
一个线程可以通过各种事件进入WAITING状态,例如对wait()方法的调用。在这种状态下,一个线程正在等待另一个线程的信号。
When a thread either finishes execution or terminates abnormally, it’ll wind up in the TERMINATED state. Threads can be interrupted, and when a thread is interrupted, it will throw InterruptedException.
当一个线程完成执行或异常终止时,它将进入TERMINATED状态。线程可以被中断,当一个线程被中断时,它将抛出InterruptedException.。
In the next sections, we’ll see InterruptedException in detail and learn how to respond to it.
在接下来的章节中,我们将详细了解InterruptedException并学习如何应对它。
3. What Is an InterruptedException?
3.什么是中断异常?
An InterruptedException is thrown when a thread is interrupted while it’s waiting, sleeping, or otherwise occupied. In other words, some code has called the interrupt() method on our thread. It’s a checked exception, and many blocking operations in Java can throw it.
当一个线程在等待、睡眠或其他情况下被打断时,会抛出一个InterruptedException。换句话说,有些代码在我们的线程上调用了interrupt()方法。这是一个受检查的异常,Java中的许多阻塞性操作都可以抛出这种异常。
3.1. Interrupts
3.1 中断
The purpose of the interrupt system is to provide a well-defined framework for allowing threads to interrupt tasks (potentially time-consuming ones) in other threads. A good way to think about interruption is that it doesn’t actually interrupt a running thread — it just requests that the thread interrupt itself at the next convenient opportunity.
中断系统的目的是提供一个定义明确的框架,允许线程中断其他线程的任务(可能是耗时的任务)。思考中断的一个好方法是,它实际上并没有中断一个正在运行的线程–它只是请求该线程在下一个方便的机会中断自己。
3.2. Blocking and Interruptible Methods
3.2.阻断和可中断方法
Threads may block for several reasons: waiting to wake up from a Thread.sleep(), waiting to acquire a lock, waiting for I/O completion, or waiting for the result of a computation in another thread, among others.
线程可能因为几个原因而阻塞:等待从Thread.sleep()中醒来,等待获得一个锁,等待I/O完成,或者等待另一个线程的计算结果,等等。
The InterruptedException is usually thrown by all blocking methods so that it can be handled and the corrective action can be performed. There are several methods in Java that throw InterruptedException. These include Thread.sleep(), Thread.join(), the wait() method of the Object class, and put() and take() methods of BlockingQueue, to name a few.
InterruptedException通常由所有阻塞方法抛出,以便可以处理并执行纠正措施。在Java中有几个方法抛出InterruptedException。这些方法包括Thread.sleep()、Thread.join()、Object类的wait()方法,以及BlockingQueue的put()和take()方法,仅此而已。
3.3. Interruption Methods in Threads
3.3.线程中的中断方法
Let’s have a quick look at some key methods of the Thread class for dealing with interrupts:
让我们快速浏览一下Thread类中处理中断的一些关键方法。
public void interrupt() { ... }
public boolean isInterrupted() { ... }
public static boolean interrupted() { ... }
Thread provides the interrupt() method for interrupting a thread, and to query whether a thread has been interrupted, we can use the isInterrupted() method. Occasionally, we may wish to test whether the current thread has been interrupted and if so, to immediately throw this exception. Here, we can use the interrupted() method:
Thread提供了用于中断线程的interrupt()方法,为了查询一个线程是否被中断,我们可以使用isInterrupted()方法。偶尔,我们可能希望测试当前线程是否已经被中断,如果是,就立即抛出这个异常。这里,我们可以使用interrupted()方法。
if (Thread.interrupted()) {
throw new InterruptedException();
}
3.4. The Interrupt Status Flag
3.4.中断状态标志
The interrupt mechanism is implemented using a flag known as the interrupt status. Each thread has a boolean property that represents its interrupted status. Invoking Thread.interrupt() sets this flag. When a thread checks for an interrupt by invoking the static method Thread.interrupted(), the interrupt status is cleared.
中断机制是通过一个被称为中断状态的标志来实现的。每个线程都有一个boolean属性,代表其中断状态。调用Thread.interrupt()会设置这个标志。 当一个线程通过调用static方法Thread.interrupted()来检查一个中断时,中断状态被清除了。
To respond to interrupt requests, we must handle InterruptedException. We’ll see how to do just that in the next section.
为了响应中断请求,我们必须处理InterruptedException.我们将在下一节看到如何做到这一点。
4. How to Handle an InterruptedException
4.如何处理InterruptedException?
It’s important to note that thread scheduling is JVM-dependent. This is natural, as JVM is a virtual machine and requires the native operating system resources to support multithreading. Hence, we can’t guarantee that our thread will never be interrupted.
值得注意的是,线程调度是依赖于JVM的。这很自然,因为JVM是一个虚拟机,需要本地操作系统资源来支持多线程。因此,我们不能保证我们的线程永远不会被打断。
An interrupt is an indication to a thread that it should stop what it’s doing and do something else. More specifically, if we’re writing some code that will be executed by an Executor or some other thread management mechanism, we need to make sure that our code responds promptly to interrupts. Otherwise, our application may lead to a deadlock.
中断是对一个线程的指示,它应该停止它正在做的事情,做一些别的事情。更具体地说,如果我们正在编写一些将由执行器或其他一些线程管理机制执行的代码,我们需要确保我们的代码对中断作出及时的响应。否则,我们的应用程序可能会导致deadlock。
There are few practical strategies for handling InterruptedException. Let’s take a look at them.
有一些处理InterruptedException的实用策略。让我们来看看它们。
4.1. Propagate the InterruptedException
4.1.传播InterruptedException
We can allow the InterruptedException to propagate up the call stack, for example, by adding a throws clause to each method in turn and letting the caller determine how to handle the interrupt. This can involve our not catching the exception or catching and rethrowing it. Let’s try to achieve this in an example:
我们可以允许InterruptedException在调用栈上传播,例如,依次为每个方法添加throws子句,让调用者决定如何处理中断。这可能涉及到我们不捕捉异常或捕捉并重新抛出异常。让我们在一个例子中尝试实现这一点。
public static void propagateException() throws InterruptedException {
Thread.sleep(1000);
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
Here, we are checking whether the thread is interrupted and if so, we throw an InterruptedException. Now, let’s call the propagateException() method:
在这里,我们要检查线程是否被中断,如果是,我们抛出一个InterruptedException。现在,让我们调用propagateException()方法。
public static void main(String... args) throws InterruptedException {
propagateException();
}
When we try to run this piece of code, we’ll receive an InterruptedException with the stack trace:
当我们试图运行这段代码时,我们会收到一个带有堆栈跟踪的InterruptedException。
Exception in thread "main" java.lang.InterruptedException
at com.baeldung.concurrent.interrupt.InterruptExample.propagateException(InterruptExample.java:16)
at com.baeldung.concurrent.interrupt.InterruptExample.main(InterruptExample.java:7)
Although this is the most sensible way to respond to the exception, sometimes we can’t throw it — for instance, when our code is a part of a Runnable. In this situation, we must catch it and restore the status. We’ll see how to handle this scenario in the next section.
尽管这是对异常作出反应的最明智的方式,但有时我们不能抛出它–例如,当我们的代码是Runnable的一部分时。在这种情况下,我们必须捕捉它并恢复其状态。我们将在下一节中看到如何处理这种情况。
4.2. Restore the Interrupt
4.2.恢复中断
There are some cases where we can’t propagate InterruptedException. For example, suppose our task is defined by a Runnable or overriding a method that doesn’t throw any checked exceptions. In such cases, we can preserve the interruption. The standard way to do this is to restore the interrupted status.
有些情况下,我们不能传播InterruptedException。例如,假设我们的任务是由Runnable定义的,或者重写一个不抛出任何检查异常的方法。在这种情况下,我们可以保留中断。这样做的标准方法是恢复中断状态。
We can call the interrupt() method again (it will set the flag back to true) so that the code higher up the call stack can see that an interrupt was issued. For example, let’s interrupt a thread and try to access its interrupted status:
我们可以再次调用interrupt()方法(它将把该标志设回true),这样在调用堆栈较高位置的代码就可以看到发出了中断。例如,让我们中断一个线程并尝试访问其中断状态。
public class InterruptExample extends Thread {
public static Boolean restoreTheState() {
InterruptExample thread1 = new InterruptExample();
thread1.start();
thread1.interrupt();
return thread1.isInterrupted();
}
}
And here’s the run() method that handles this interrupt and restores the interrupted status:
这里是处理这个中断并恢复中断状态的run()方法。
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); //set the flag back to <code>true
} }
Finally, let’s test the status:
最后,让我们测试一下状态。
assertTrue(InterruptExample.restoreTheState());
Although Java exceptions cover all the exceptional cases and conditions, we might want to throw a specific custom exception unique to the code and business logic. Here, we can create our custom exception to handle the interrupt. We’ll see it in the next section.
尽管Java异常涵盖了所有的特殊情况和条件,但我们可能想抛出一个代码和业务逻辑所特有的特定自定义异常。在这里,我们可以创建我们的自定义异常来处理中断。我们将在下一节中看到它。
4.3. Custom Exception Handling
4.3.自定义异常处理
Custom exceptions provide the flexibility to add attributes and methods that are not part of a standard Java exception. Hence, it’s perfectly valid to handle the interrupt in a custom way, depending on the circumstances.
自定义异常提供了灵活性,可以添加不属于标准Java异常的属性和方法。因此,根据具体情况,以自定义的方式处理中断是完全有效的。
We can complete additional work to allow the application to handle the interrupt request gracefully. For instance, when a thread is sleeping or waiting on an I/O operation, and it receives the interrupt, we can close any resources before terminating the thread.
我们可以完成额外的工作,使应用程序能够优雅地处理中断请求。例如,当一个线程正在睡眠或等待I/O操作时,如果它收到中断,我们可以在终止线程之前关闭任何资源。
Let’s create a custom checked exception called CustomInterruptedException:
让我们创建一个名为CustomInterruptedException的自定义检查异常。
public class CustomInterruptedException extends Exception {
CustomInterruptedException(String message) {
super(message);
}
}
We can throw our CustomInterruptedException when the thread is interrupted:
当线程被中断时,我们可以抛出我们的CustomInterruptedException 。
public static void throwCustomException() throws Exception {
Thread.sleep(1000);
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
throw new CustomInterruptedException("This thread was interrupted");
}
}
Let’s also see how we can check whether the exception is thrown with the correct message:
我们也来看看如何检查异常是否以正确的信息被抛出。
@Test
public void whenThrowCustomException_thenContainsExpectedMessage() {
Exception exception = assertThrows(CustomInterruptedException.class, () -> InterruptExample.throwCustomException());
String expectedMessage = "This thread was interrupted";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
Similarly, we can handle the exception and restore the interrupted status:
类似地,我们可以处理异常并恢复中断的状态。
public static Boolean handleWithCustomException() throws CustomInterruptedException{
try {
Thread.sleep(1000);
Thread.currentThread().interrupt();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CustomInterruptedException("This thread was interrupted...");
}
return Thread.currentThread().isInterrupted();
}
We can test the code by checking the interrupted status to make sure it returns true:
我们可以通过检查中断的状态来测试代码,确保它返回true。
assertTrue(InterruptExample.handleWithCustomException());
5. Conclusion
5.总结
In this tutorial, we saw different ways to handle the InterruptedException. If we handle it correctly, we can balance the responsiveness and robustness of the application. And, as always, the code snippets used here are available over on GitHub.
在本教程中,我们看到了处理InterruptedException的不同方法。如果我们正确地处理它,就可以平衡应用程序的响应性和健壮性。而且,像往常一样,这里所使用的代码片段可在GitHub上获得。