`
sole
  • 浏览: 141513 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java theory and practice: Dealing with Interrupte

阅读更多
Many Java™ language methods, such as Thread.sleep() and Object.wait(), throw InterruptedException. You can't ignore it because it's a checked exception, but what should you do with it? In this month's Java theory and practice, concurrency expert Brian Goetz explains what InterruptedException means, why it is thrown, and what you should do when you catch one.

<script type="text/javascript"></script>

<!----><!----><!----><!----><!---->

This story is probably familiar: You're writing a test program and you need to pause for some amount of time, so you call Thread.sleep(). But then the compiler or IDE balks that you haven't dealt with the checked InterruptedException. What is InterruptedException, and why do you have to deal with it?

The most common response to InterruptedException is to swallow it -- catch it and do nothing (or perhaps log it, which isn't any better) -- as we'll see later in Listing 4. Unfortunately, this approach throws away important information about the fact that an interrupt occurred, which could compromise the application's ability to cancel activities or shut down in a timely manner.

Blocking methods

When a method throws InterruptedException, it is telling you several things in addition to the fact that it can throw a particular checked exception. It is telling you that it is a blocking method and that it will make an attempt to unblock and return early -- if you ask nicely.

A blocking method is different from an ordinary method that just takes a long time to run. The completion of an ordinary method is dependent only on how much work you've asked it to do and whether adequate computing resources (CPU cycles and memory) are available. The completion of a blocking method, on the other hand, is also dependent on some external event, such as timer expiration, I/O completion, or the action of another thread (releasing a lock, setting a flag, or placing a task on a work queue). Ordinary methods complete as soon as their work can be done, but blocking methods are less predictable because they depend on external events. Blocking methods can compromise responsiveness because it can be hard to predict when they will complete.

Because blocking methods can potentially take forever if the event they are waiting for never occurs, it is often useful for blocking operations to be cancelable. (It is often useful for long-running non-blocking methods to be cancelable as well.) A cancelable operation is one that can be externally moved to completion in advance of when it would ordinarily complete on its own. The interruption mechanism provided by Thread and supported by Thread.sleep() and Object.wait() is a cancellation mechanism; it allows one thread to request that another thread stop what it is doing early. When a method throws InterruptedException, it is telling you that if the thread executing the method is interrupted, it will make an attempt to stop what it is doing and return early and indicate its early return by throwing InterruptedException. Well-behaved blocking library methods should be responsive to interruption and throw InterruptedException so they can be used within cancelable activities without compromising responsiveness.

Thread interruption

Every thread has a Boolean property associated with it that represents its interrupted status. The interrupted status is initially false; when a thread is interrupted by some other thread through a call to Thread.interrupt(), one of two things happens. If that thread is executing a low-level interruptible blocking method like Thread.sleep(), Thread.join(), or Object.wait(), it unblocks and throws InterruptedException. Otherwise, interrupt() merely sets the thread's interruption status. Code running in the interrupted thread can later poll the interrupted status to see if it has been requested to stop what it is doing; the interrupted status can be read with Thread.isInterrupted() and can be read and cleared in a single operation with the poorly named Thread.interrupted().

Interruption is a cooperative mechanism. When one thread interrupts another, the interrupted thread does not necessarily stop what it is doing immediately. Instead, interruption is a way of politely asking another thread to stop what it is doing if it wants to, at its convenience. Some methods, like Thread.sleep(), take this request seriously, but methods are not required to pay attention to interruption. Methods that do not block but that still may take a long time to execute can respect requests for interruption by polling the interrupted status and return early if interrupted. You are free to ignore an interruption request, but doing so may compromise responsiveness.

One of the benefits of the cooperative nature of interruption is that it provides more flexibility for safely constructing cancelable activities. We rarely want an activity to stop immediately; program data structures could be left in an inconsistent state if the activity were canceled mid-update. Interruption allows a cancelable activity to clean up any work in progress, restore invariants, notify other activities of the cancellation, and then terminate.

 

 

Dealing with InterruptedException

If throwing InterruptedException means that a method is a blocking method, then calling a blocking method means that your method is a blocking method too, and you should have a strategy for dealing with InterruptedException. Often the easiest strategy is to throw InterruptedException yourself, as shown in the putTask() and getTask() methods in Listing 1. Doing so makes your method responsive to interruption as well and often requires nothing more than adding InterruptedException to your throws clause.


Listing 1. Propagating InterruptedException to callers by not catching it

public class TaskQueue {
    private static final int MAX_TASKS = 1000;

    private BlockingQueue<Task> queue 
        = new LinkedBlockingQueue<Task>(MAX_TASKS);

    public void putTask(Task r) throws InterruptedException { 
        queue.put(r);
    }

    public Task getTask() throws InterruptedException { 
        return queue.take();
    }
}

 

Sometimes it is necessary to do some amount of cleanup before propagating the exception. In this case, you can catch InterruptedException, perform the cleanup, and then rethrow the exception. Listing 2, a mechanism for matching players in an online game service, illustrates this technique. The matchPlayers() method waits for two players to arrive and then starts a new game. If it is interrupted after one player has arrived but before the second player arrives, it puts that player back on the queue before rethrowing the InterruptedException, so that the player's request to play is not lost.


Listing 2. Performing task-specific cleanup before rethrowing InterruptedException

public class PlayerMatcher {
    private PlayerSource players;

    public PlayerMatcher(PlayerSource players) { 
        this.players = players; 
    }

    public void matchPlayers() throws InterruptedException { 
        Player playerOne, playerTwo;
         try {
             while (true) {
                 playerOne = playerTwo = null;
                 // Wait for two players to arrive and start a new game
                 playerOne = players.waitForPlayer(); // could throw IE
                 playerTwo = players.waitForPlayer(); // could throw IE
                 startNewGame(playerOne, playerTwo);
             }
         }
         catch (InterruptedException e) {  
             // If we got one player and were interrupted, put that player back
             if (playerOne != null)
                 players.addFirst(playerOne);
             // Then propagate the exception
             throw e;
         }
    }
}

 

Don't swallow interrupts

Sometimes throwing InterruptedException is not an option, such as when a task defined by Runnable calls an interruptible method. In this case, you can't rethrow InterruptedException, but you also do not want to do nothing. When a blocking method detects interruption and throws InterruptedException, it clears the interrupted status. If you catch InterruptedException but cannot rethrow it, you should preserve evidence that the interruption occurred so that code higher up on the call stack can learn of the interruption and respond to it if it wants to. This task is accomplished by calling interrupt() to "reinterrupt" the current thread, as shown in Listing 3. At the very least, whenever you catch InterruptedException and don't rethrow it, reinterrupt the current thread before returning.


Listing 3. Restoring the interrupted status after catching InterruptedException

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

 

The worst thing you can do with InterruptedException is swallow it -- catch it and neither rethrow it nor reassert the thread's interrupted status. The standard approach to dealing with an exception you didn't plan for -- catch it and log it -- also counts as swallowing the interruption because code higher up on the call stack won't be able to find out about it. (Logging InterruptedException is also just silly because by the time a human reads the log, it is too late to do anything about it.) Listing 4 shows the all-too-common pattern of swallowing an interrupt:


Listing 4. Swallowing an interrupt -- don't do this

// Don't do this 
public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException swallowed) { 
             /* DON'T DO THIS - RESTORE THE INTERRUPTED STATUS INSTEAD */
         }
    }
}

 

If you cannot rethrow InterruptedException, whether or not you plan to act on the interrupt request, you still want to reinterrupt the current thread because a single interruption request may have multiple "recipients." The standard thread pool (ThreadPoolExecutor) worker thread implementation is responsive to interruption, so interrupting a task running in a thread pool may have the effect of both canceling the task and notifying the execution thread that the thread pool is shutting down. If the task were to swallow the interrupt request, the worker thread might not learn that an interrupt was requested, which could delay the application or service shutdown.

 


Back to top

 

Implementing cancelable tasks

Nothing in the language specification gives interruption any specific semantics, but in larger programs, it is difficult to maintain any semantics for interruption other than cancellation. Depending on the activity, a user could request cancellation through a GUI or through a network mechanism such as JMX or Web Services. It could also be requested by program logic. For example, a Web crawler might automatically shut itself down if it detects that the disk is full, or a parallel algorithm might start multiple threads to search different regions of the solution space and cancel them once one of them finds a solution.

Just because a task is cancelable does not mean it needs to respond to an interrupt request immediately. For tasks that execute code in a loop, it is common to check for interruption only once per loop iteration. Depending on how long the loop takes to execute, it could take some time before the task code notices the thread has been interrupted (either by polling the interrupted status with Thread.isInterrupted() or by calling a blocking method). If the task needs to be more responsive, it can poll the interrupted status more frequently. Blocking methods usually poll the interrupted status immediately on entry, throwing InterruptedException if it is set to improve responsiveness.

The one time it is acceptable to swallow an interrupt is when you know the thread is about to exit. This scenario only occurs when the class calling the interruptible method is part of a Thread, not a Runnable or general-purpose library code, as illustrated in Listing 5. It creates a thread that enumerates prime numbers until it is interrupted and allows the thread to exit upon interruption. The prime-seeking loop checks for interruption in two places: once by polling the isInterrupted() method in the header of the while loop and once when it calls the blocking BlockingQueue.put() method.


Listing 5. Interrupts can be swallowed if you know the thread is about to exit

public class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

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

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime());
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() { interrupt(); }
}

 

Noninterruptible blocking

Not all blocking methods throw InterruptedException. The input and output stream classes may block waiting for an I/O to complete, but they do not throw InterruptedException, and they do not return early if they are interrupted. However, in the case of socket I/O, if a thread closes the socket, blocking I/O operations on that socket in other threads will complete early with a SocketException. The nonblocking I/O classes in java.nio also do not support interruptible I/O, but blocking operations can similarly be canceled by closing the channel or requesting a wakeup on the Selector. Similarly, attempting to acquire an intrinsic lock (enter a synchronized block) cannot be interrupted, but ReentrantLock supports an interruptible acquisition mode.

Noncancelable tasks

Some tasks simply refuse to be interrupted, making them noncancelable. However, even noncancelable tasks should attempt to preserve the interrupted status in case code higher up on the call stack wants to act on the interruption after the noncancelable task completes. Listing 6 shows a method that waits on a blocking queue until an item is available, regardless of whether it is interrupted. To be a good citizen, it restores the interrupted status in a finally block after it is finished, so as not to deprive callers of the interruption request. (It can't restore the interrupted status earlier, as it would cause an infinite loop -- BlockingQueue.take() could poll the interrupted status immediately on entry and throws InterruptedException if it finds the interrupted status set.)


Listing 6. Noncancelable task that restores interrupted status before returning

public Task getNextTask(BlockingQueue<Task> queue) {
    boolean interrupted = false;
    try {
        while (true) {
            try {
                return queue.take();
            } catch (InterruptedException e) {
                interrupted = true;
                // fall through and retry
            }
        }
    } finally {
        if (interrupted)
            Thread.currentThread().interrupt();
    }
}
    

 


Back to top

 

Summary

You can use the cooperative interruption mechanism provided by the Java platform to construct flexible cancellation policies. Activities can decide if they are cancelable or not, how responsive they want to be to interruption, and they can defer interruption to perform task-specific cleanup if returning immediately would compromise application integrity. Even if you want to completely ignore interruption in your code, make sure to restore the interrupted status if you catch InterruptedException and do not rethrow it so that the code that calls it is not deprived of the knowledge that an interrupt occurred.

 

Resources

Learn

  • Java 2 Platform API Specifications: API specifications for the Java 2 Platform, Standard Edition, version 1.4.2.

  • Java Concurrency in Practice (Addison-Wesley Professional, Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea, June 2006): Chapter 7 of this how-to manual for developing concurrent programs in the Java language addresses the details of interruption and cancellation in much greater depth.

  • Java theory and practice: The complete series by Brian Goetz.

  • The Java technology zone: Hundreds of articles about every aspect of Java programming.


Get products and technologies


Discuss

 

About the author

 

Brian Goetz has been a professional software developer for over 18 years. He is a Principal Consultant at Quiotix, a software development and consulting firm located in Los Altos, California, and he serves on several JCP Expert Groups. Brian's book, Java Concurrency In Practice, will be published in May 2006 by Addison-Wesley. See Brian's published and upcoming articles in popular industry publications.

 

中文翻译:http://www.ibm.com/developerworks/cn/java/j-jtp05236.html

分享到:
评论

相关推荐

    Image Processing:Dealing With Texture

    《图像处理:处理纹理》是图像处理领域内一部权威性的著作,由Maria Petrou和Pedro Garcia Sevilla两位学者共同编写,他们分别来自英国伦敦帝国理工学院和西班牙卡斯特利翁的Jaume I大学。该书由全球知名的学术出版...

    Multi-Label classification: Dealing with Imbalance by Combining Labels

    ### 多标签分类:通过合并标签处理不平衡问题 #### 摘要与介绍 本文讨论了一个在多标签分类(Multi-Label Classification, MLC)领域中的常见问题——数据不平衡,并提出了一种新颖的方法来解决这一难题。...

    ASTM E178 - 21 Standard Practice for Dealing With Outlying Obser

    标题中的"ASTM E178 - 21"指的是美国材料与...通过阅读提供的"ASTM E178 - 21 Standard Practice for Dealing With Outlying Observations - 完整英文版(11页).pdf"文件,可以深入学习这一标准的细节和具体实施步骤。

    Data.Analysis.with.R.17852881

    Key Features Load, manipulate and analyze data from different sources ...Chapter 11: Dealing with Messy Data Chapter 12: Dealing with Large Data Chapter 13: Reproducibility and Best Practices

    HTML5.Games.Creating.Fun.with.HTML5.CSS3.and.WebGL.2nd.Edition

    Dealing with backwards compatibility Generating level data Making iOS and Android web apps Taking your game offline Using Web Workers Persistent Game Data Drawing with Canvas Capturing player input ...

    Dealing with Undesirable Outputs in DEA: A Slacks-based Measure(SBM) Approach

    在进行数据包络分析(Data Envelopment Analysis, DEA)研究时,经常需要面对的问题之一是如何处理非期望产出(undesirable outputs)。传统DEA模型在处理生产效率分析时通常假设决策单元(Decision Making Units, ...

    Hadoop权威

    标题与描述均提到“Hadoop权威”,这表明文章或资源主要聚焦于Hadoop技术的深入讲解与权威解读。Hadoop是一种开源软件框架,用于分布式存储和处理大型数据集。它由Apache基金会维护,广泛应用于大数据处理领域。...

    Head First Python , Second Edition by Paul Barry 2016年出版 英文版

    113⁄4 A Little Bit of Threading: Dealing with Waiting 461 12 Advanced Iteration: Looping like Crazy 477 A Installing: Installing Python 521 B Pythonanywhere: Deploying Your Webapp 529 C Top Ten ...

    the art and practice of leadership coaching

    Leadership coaching has become vitally important to today?... Packed with cutting-edge ideas and proven best practices, this is the definitive source of information for anyone dealing with coaching.

    Docker in Practice, 2nd Edition

    Table of Contents: PART 1 - DOCKER FUNDAMENTALS Chapter 1 Discovering Docker Chapter 2 Understanding Docker: Inside The Engine Room ...Chapter 3 Docker In Production: Dealing With Challenges

    Java2核心技术卷I+卷2:基础知识(第8版) 代码

    Dealing with Errors 552 Catching Exceptions 559 Tips for Using Exceptions 568 Using Assertions 571 Logging 575 Debugging Tips 591 Using a Debugger 607 Chapter 12: Generic Programming 613 ...

    八年级英语Dealing with troublePPT课件.pptx

    这篇PPT课件是针对八年级英语教学的内容,主题为“应对困难”(Dealing with trouble)。通过一系列的填空练习和情景模拟,旨在帮助学生掌握如何在不同情况下正确处理问题,尤其是面对紧急情况时的应对策略。以下是...

    ASP.NET Web API 2, 2nd Edition.pdf

    ■■Chapter 7: Dealing with Relationships Partial Updates and Other Complexities 157 ■■Chapter 8: Supporting Diverse Clients 209 ■■Chapter 9: Completing the Picture 221 Index "&gt;About the Authors ...

Global site tag (gtag.js) - Google Analytics