安徽海川建设公司网站一级消防工程师考试资料

当前位置: 首页 > news >正文

安徽海川建设公司网站,一级消防工程师考试资料,百度收录查询工具官网,网络营销具体做什么如果外部代码能在某个操作正常完成之前将其置入“完成”状态#xff0c;那么这个操作就可以称为可取消的(Cancellable)。取消某个操作的原因很多#xff1a; 用户请求取消。用户点击图形界面程序中的“取消”按钮#xff0c;或者通过管理接口来发出取消请求,例如JMX (Java …如果外部代码能在某个操作正常完成之前将其置入“完成”状态那么这个操作就可以称为可取消的(Cancellable)。取消某个操作的原因很多 用户请求取消。用户点击图形界面程序中的“取消”按钮或者通过管理接口来发出取消请求,例如JMX (Java Management Extensions)。 有时间限制的操作。例如某个应用程序需要在有限时间内搜索问题空间并在这个时间内选择最佳的解决方案。当计时器超时时需要取消所有正在搜索的任务。 应用程序事件。例如应用程序对某个问题空间进行分解并搜索从而使不同的任务可以搜索问题空间中的不同区域。当其中一个任务找到了解决方案时所有其他仍在搜索的任务都 虽然Thread. stop和suspend等方法提供了这样的机制但由于存在着一些严重的缺陷因此应该避免使用。请参见http://java.sun.com/j2se/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html了解对这些问题的详细说明。 将被取消。 错误。网页爬虫程序搜索相关的页面并将页面或摘要数据保存到硬盘。当一个爬虫任务发生错误时(例如磁盘空间已满)那么所有搜索任务都会取消此时可能会记录它们的当前状态以便稍后重新启动。 关闭。当一个程序或服务关闭时必须对正在处理和等待处理的工作执行某种操作。在平缓的关闭过程中当前正在执行的任务将继续执行直到完成而在立即关闭过程中当前的任务则可能取消。 在Java 中没有一种安全的抢占式方法来停止线程因此也就没有安全的抢占式方法来停止任务。只有一些协作式的机制使请求取消的任务和代码都遵循一种协商好的协议。 其中一种协作机制能设置某个“已请求取消(Cancellation Requested)”标志,而任务将定期地查看该标志。如果设置了这个标志那么任务将提前结束。程序清单7-1中就使用了这项技术,其中的PrimeGenerator 持续地枚举素数,直到它被取消。cancel方法将设置cancelled标志并且主循环在搜索下一个素数之前会首先检查这个标志。(为了使这个过程能可靠地工作标志cancelled必须为volatile 类型。) public class PrimeGenerator implements Runnable { GuardedBy(this) private final ListBigIntegerprimes new ArrayListBigInteger(); private volatile boolean cancelled; public void run(){ BigInteger p BigInteger. ONE; while (Icancelled ){ p p. nextProbablePrime(); synchronized (this){ primes. add(p); } } } public void cancel(){cancelled true;} public synchronized ListBigIntegerget(){ return new ArrayListBigInteger(primes); } } 程序清单7-2 给出了这个类的使用示例即让素数生成器运行1秒钟后取消。素数生成器通常并不会刚好在运行1秒钟后停止因为在请求取消的时刻和run方法中循环执行下一次检查之间可能存在延迟。cancel方法由finally 块调用从而确保即使在调用sleep 时被中断也能取消素数生成器的执行。如果cancel 没有被调用那么搜索素数的线程将永远运行下去不断消耗CPU的时钟周期并使得JVM 不能正常退出。 程序清单7-2      一个仅运行一秒钟的素数生成器                   ListBigIntegeraSecond0fPrimes() throws InterruptedException { PrimeGenerator generator new PrimeGenerator(); new Thread (generator). start(); try { SECONDS. sleep(1); }finally { generator. cancel(); } return generator. get(); } 一个可取消的任务必须拥有取消策略(Cancellation Policy)在这个策略中将详细地定义取消操作的“How”、“When”以及“What”,即其他代码如何(How)请求取消该任务,任务在何时(When)检查是否已经请求了取消以及在响应取消请求时应该执行哪些(What)操作。 考虑现实世界中停止支付(Stop-Payment)支票的示例。银行通常都会规定如何提交一个停止支付的请求在处理这些请求时需要做出哪些响应性保证以及当支付中断后需要遵守哪些流程(例如通知该事务中涉及的其他银行以及对付款人的账户进行费用评估)。这些流程和保证放在一起就构成了支票支付的取消策略。 PrimeGenerator 使用了一种简单的取消策略客户代码通过调用cancel来请求取消PrimeGenerator在每次搜索素数前首先检查是否存在取消请求如果存在则退出。 中断 PrimeGenerator中的取消机制最终会使得搜索素数的任务退出但在退出过程中需要花费一定的时间。然而如果使用这种方法的任务调用了一个阻塞方法例如BlockingQueue. put那么可能会产生一个更严重的问题——任务可能永远不会检查取消标志因此永远不会结束。 在程序清单7-3 中的BrokenPrimeProducer就说明了这个问题。生产者线程生成素数,并将它们放入一个阻塞队列。如果生产者的速度超过了消费者的处理速度队列将被填满 put 方法也会阻塞。当生产者在put方法中阻塞时如果消费者希望取消生产者任务那么将发生什么情况?它可以调用cancel方法来设置cancelled标志但此时生产者却永远不能检查这个标志因为它无法从阻塞的put 方法中恢复过来(因为消费者此时已经停止从队列中取出素数所以put 方法将一直保持阻塞状态)。 this. queue queue; } public void run(){ try { BigInteger p BigInteger. ONE; while (Icancelled) queue. put(pp. nextProbablePrime()); }catch (InterruptedException consumed){} } public void cancel(){cancelled true;} } void consumePrimes() throws InterruptedException { BlockingQueueBigIntegerprimes ···; BrokenPrimeProducer producer new BrokenPrimeProducer(primes); producer. start(); try { while (needMorePrimes()) consume(primes. take()); }finally { producer. cancel(); } } 一些特殊的阻塞库的方法支持中断。线程中断是一种协作机制线程可以通过这种机制来通知另一个线程告诉它在合适的或者可能的情况下停止当前工作并转而执行其他的工作。 在Java的API或语言规范中并没有将中断与任何取消语义关联起来但实际上如果在取消之外的其他操作中使用中断那么都是不合适的并且很难支撑起更大的应用。 每个线程都有一个boolean类型的中断状态。当中断线程时这个线程的中断状态将被设置为true。在Thread 中包含了中断线程以及查询线程中断状态的方法如程序清单7-4所示。interrupt方法能中断目标线程而isInterrupted 方法能返回目标线程的中断状态。静态的interrupted 方法将清除当前线程的中断状态并返回它之前的值这也是清除中断状态的唯一方法。 程序清单7-4               Thread 中的中断方法                   public class Thread { public void interrupt(){…} public boolean isInterrupted(){……} public static boolean interrupted(){…} … 阻塞库方法,例如Thread. sleep 和Object. wait等,都会检查线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括清除中断状态抛出InterruptedException表示阻塞操作由于中断而提前结束。JVM 并不能保证阻塞方法检测到中断的速度但在实际情况中响应速度还是非常快的。 当线程在非阻塞状态下中断时它的中断状态将被设置然后根据将被取消的操作来检查中断状态以判断发生了中断。通过这样的方法中断操作将变得“有黏性”——如果不触发InterruptedException,那么中断状态将一直保持,直到明确地清除中断状态。 调用interrupt并不意味着立即停止目标线程正在进行的工作而只是传递了请求中断的消息。 对中断操作的正确理解是它并不会真正地中断一个正在运行的线程而只是发出中断请求然后由线程在下一个合适的时刻中断自己。(这些时刻也被称为取消点)。有些方法例如wait、sleep 和join等将严格地处理这种请求当它们收到中断请求或者在开始执行时发现某个已被设置好的中断状态时将抛出一个异常。设计良好的方法可以完全忽略这种请求只要它们能使调用代码对中断请求进行某种处理。设计糟糕的方法可能会屏蔽中断请求从而导致调用栈中的其他代码无法对中断请求作出响应。 在使用静态的interrupted 时应该小心因为它会清除当前线程的中断状态。如果在调用interrupted 时返回了true那么除非你想屏蔽这个中断否则必须对它进行处理——可以抛出InterruptedException,或者通过再次调用interrupt来恢复中断状态,如程序清单5-10 所示。 BrokenPrimeProducer 说明了一些自定义的取消机制无法与可阻塞的库函数实现良好交互的原因。如果任务代码能够响应中断那么可以使用中断作为取消机制并且利用许多库类中提供的中断支持。 通常中断是实现取消的最合理方式。 BrokenPrimeProducer 中的问题很容易解决(和简化):使用中断而不是boolean标志来请求取消如程序清单7-5所示。在每次迭代循环中有两个位置可以检测出中断在阻塞的put 方法调用中以及在循环开始处查询中断状态时。由于调用了阻塞的put 方法因此这里并不一定需要进行显式的检测但执行检测却会使PrimeProducer 对中断具有更高的响应性因为它是在启动寻找素数任务之前检查中断的而不是在任务完成之后。如果可中断的阻塞方法的调用频率并不高不足以获得足够的响应性那么显式地检测中断状态能起到一定的帮助作用。 程序清单7-5                     通过中断来取消                         class PrimeProducer extends Thread { private final BlockingQueueBigIntegerqueue; PrimeProducer(BlockingQueueBigIntegerqueue){ this. queue queue; } public void run(){ try { BigInteger p BigInteger. ONE; while (!Thread. currentThread(). isInterrupted()) queue. put (p p. nextProbablePrime()); }catch (InterruptedException consumed){ /*   允许线程退出      */ } } public void cancel(){interrupt();} } 中断策略 正如任务中应该包含取消策略一样线程同样应该包含中断策略。中断策略规定线程如何解释某个中断请求——当发现中断请求时应该做哪些工作(如果需要的话)哪些工作单元对于中断来说是原子操作以及以多快的速度来响应中断。 最合理的中断策略是某种形式的线程级(Thread-Level)取消操作或服务级(Service-Level)取消操作尽快退出在必要时进行清理通知某个所有者该线程已经退出。此外还可以建立其他的中断策略例如暂停服务或重新开始服务但对于那些包含非标准中断策略的线程或线程池只能用于能知道这些策略的任务中。 区分任务和线程对中断的反应是很重要的。一个中断请求可以有一个或多个接收者——中断线程池中的某个工作者线程同时意味着“取消当前任务”和“关闭工作者线程”。 任务不会在其自己拥有的线程中执行而是在某个服务(例如线程池)拥有的线程中执行。对于非线程所有者的代码来说(例如对于线程池而言任何在线程池实现以外的代码)应该小心地保存中断状态这样拥有线程的代码才能对中断做出响应即使“非所有者”代码也可以做出响应。(当你为一户人家打扫房屋时即使主人不在也不应该把在这段时间内收到的邮件扔掉而应该把邮件收起来等主人回来以后再交给他们处理尽管你可以阅读他们的杂志。) 这就是为什么大多数可阻塞的库函数都只是抛出InterruptedException 作为中断响应。它们永远不会在某个由自己拥有的线程中运行因此它们为任务或库代码实现了最合理的取消策略尽快退出执行流程并把中断信息传递给调用者从而使调用栈中的上层代码可以采取进一步的操作。 当检查到中断请求时任务并不需要放弃所有的操作——它可以推迟处理中断请求并直到某个更合适的时刻。因此需要记住中断请求并在完成当前任务后抛出InterruptedException或者表示已收到中断请求。这项技术能够确保在更新过程中发生中断时数据结构不会被破坏。 任务不应该对执行该任务的线程的中断策略做出任何假设除非该任务被专门设计为在服务中运行并且在这些服务中包含特定的中断策略。无论任务把中断视为取消还是其他某个中断响应操作都应该小心地保存执行线程的中断状态。如果除了将InterruptedException 传递给调用者外还需要执行其他操作那么应该在捕获InterruptedException之后恢复中断状态 Thread. currentThread(). interrupt(); 正如任务代码不应该对其执行所在的线程的中断策略做出假设执行取消操作的代码也不应该对线程的中断策略做出假设。线程应该只能由其所有者中断所有者可以将线程的中断策略信息封装到某个合适的取消机制中例如关闭(shutdown)方法。 由于每个线程拥有各自的中断策略因此除非你知道中断对该线程的含义否则就不应该中断这个线程。 批评者曾嘲笑Java 的中断功能因为它没有提供抢占式中断机制而且还强迫开发人员必须处理InterruptedException。然而通过推迟中断请求的处理开发人员能制定更灵活的中断策略从而使应用程序在响应性和健壮性之间实现合理的平衡。 响应中断 在5.4节中,当调用可中断的阻塞函数时,例如Thread. sleep 或BlockingQueue. put等,有两种实用策略可用于处理InterruptedException: ·传递异常(可能在执行某个特定于任务的清除操作之后)从而使你的方法也成为可中断的阻塞方法。 ·恢复中断状态从而使调用栈中的上层代码能够对其进行处理。 传递InterruptedException 与将InterruptedException添加到throws子句中一样容易,如程序清单7-6中的getNextTask所示。 程序清单7-6将InterruptedException传递给调用者                               BlockingQueueTaskqueue; public Task getNextTask() throws InterruptedException { 如果不想或无法传递InterruptedException(或许通过Runnable 来定义任务),那么需要寻找另一种方式来保存中断请求。一种标准的方法就是通过再次调用interrupt来恢复中断状态。你不能屏蔽InterruptedException,例如在catch 块中捕获到异常却不做任何处理,除非在你的代码中实现了线程的中断策略。虽然PrimeProducer 屏蔽了中断但这是因为它已经知道线程将要结束因此在调用栈中已经没有上层代码需要知道中断信息。由于大多数代码并不知道它们将在哪个线程中运行因此应该保存中断状态。 只有实现了线程中断美味的代码才可以拼成中断铸装。在常规的任务和群代码中标 不应该屏蔽中断请求 对于一些不支持取消但仍可以调用可中断阻塞方法的操作它们必须在循环中调用这些方法并在发现中断后重新尝试。在这种情况下它们应该在本地保存中断状态并在返回前恢复状态而不是在捕获InterruptedException时恢复状态如程序清单7-7所示。如果过早地设置中断状态就可能引起无限循环因为大多数可中断的阻塞方法都会在入口处检查中断状态并且当发现该状态已被设置时会立即抛出InterruptedException。(通常可中断的方法会在阻塞或进行重要的工作前首先检查中断从而尽快地响应中断)。 public Task getNextTask(BlockingQueueTaskgt;queue){ boolean interrupted false; try { while (true){ try { return queue. take(); }catch (InterruptedException e){ interrupted true; //重新尝试 } } }finally { if (interrupted) Thread. currentThread(). interrupt(); } } 如果代码不会调用可中断的阻塞方法那么仍然可以通过在任务代码中轮询当前线程的中断状态来响应中断。要选择合适的轮询频率就需要在效率和响应性之间进行权衡。如果响应性要求较高那么不应该调用那些执行时间较长并且不响应中断的方法从而对可调用的库代码进行一些限制。 在取消过程中可能涉及除了中断状态之外的其他状态。中断可以用来获得线程的注意并且由中断线程保存的信息可以为中断的线程提供进一步的指示。(当访问这些信息时要确保使用同步。) 例如当一个由ThreadPoolExecutor 拥有的工作者线程检测到中断时它会检查线程池是否正在关闭。如果是它会在结束之前执行一些线程池清理工作否则它可能创建一个新线程将线程池恢复到合理的规模。 示例:计时运行 许多问题永远也无法解决(例如枚举所有的素数)而某些问题能很快得到答案也可能永远得不到答案。在这些情况下如果能够指定“最多花10分钟搜索答案”或者“枚举出在10分钟内能找到的答案”那么将是非常有用的。 程序清单7-2 中的aSecondOfPrimes方法将启动一个PrimeGenerator,并在1秒钟后中断。尽管PrimeGenerator可能需要超过1秒的时间才能停止但它最终会发现中断然后停止并使线程结束。在执行任务时的另一个方面是你希望知道在任务执行过程中是否会抛出异常。 如果PrimeGenerator 在指定时限内抛出了一个未检查的异常那么这个异常可能会被忽略因为素数生成器在另一个独立的线程中运行而这个线程并不会显式地处理异常。 在程序清单7-8 中给出了在指定时间内运行一个任意的Runnable的示例。它在调用线程中运行任务并安排了一个取消任务在运行指定的时间间隔后中断它。这解决了从任务中抛出未检查异常的问题因为该异常会被timedRun的调用者捕获。 cancelExec. schedule(new Runnable(){ public void run(){taskThread. interrupt();} }, timeout, unit); r. run ( ) ; } 这是一种非常简单的方法但却破坏了以下规则在中断线程之前应该了解它的中断策略。由于timedRun可以从任意一个线程中调用因此它无法知道这个调用线程的中断策略。如果任务在超时之前完成那么中断timedRun所在线程的取消任务将在timedRun返回到调用者之后启动。我们不知道在这种情况下将运行什么代码但结果一定是不好的。(可以使用schedule返回的ScheduledFuture 来取消这个取消任务以避免这种风险这种做法虽然可行但却非常复杂。) 而且如果任务不响应中断那么timedRun会在任务结束时才返回此时可能已经超过了指定的时限(或者还没有超过时限)。如果某个限时运行的服务没有在指定的时间内返回那么将对调用者带来负面影响。 在程序清单7-9 中解决了aSecondOfPrimes的异常处理问题以及之前解决方案中的问题。执行任务的线程拥有自己的执行策略即使任务不响应中断限时运行的方法仍能返回到它的调用者。在启动任务线程之后timedRun将执行一个限时的join 方法。在join 返回后它将检查任务中是否有异常抛出如果有的话则会在调用timedRun的线程中再次抛出该异常。由于Throwable将在两个线程之间共享因此该变量被声明为volatile类型从而确保安全地将其从任务线程发布到timedRun线程。 public void run(){ try  {  r. run() ;} catch (Throwable t){this. t t;} } void rethrow(){ if  ( t ! null) throw launderThrowable(t); } } RethrowableTask task new RethrowableTask(); final Thread taskThread new Thread (task); taskThread. start(); cancelExec. schedule(new Runnable(){ public void run(){taskThread. interrupt();} }, timeout, unit); taskThread. join(unit. toMillis(timeout)); task. rethrow(); } 在这个示例的代码中解决了前面示例中的问题但由于它依赖于一个限时的join因此存在着join的不足无法知道执行控制是因为线程正常退出而返回还是因为join 超时而返回。 通过Future 来实现取消 我们已经使用了一种抽象机制来管理任务的生命周期处理异常以及实现取消即Future。通常使用现有库中的类比自行编写更好因此我们将继续使用Future和任务执行框架来构建timedRun。 ExecutorService. submit将返回一个Future 来描述任务。Future拥有一个cancel 方法,该方法带有一个boolean 类型的参数mayInterruptIfRunning,表示取消操作是否成功。(这只是表示任务是否能够接收中断而不是表示任务是否能检测并处理中断。)如果mayInterruptIfRunning 为true 并且任务当前正在某个线程中运行那么这个线程能被中断。如果这个参数为false那么意味着“若任务还没有启动就不要运行它”这种方式应该用于那些不处理中断的任务中。 除非你清楚线程的中断策略否则不要中断线程那么在什么情况下调用cancel可以将参数指定为true?执行任务的线程是由标准的Executor 创建的它实现了一种中断策略使得任务可以通过中断被取消所以如果任务在标准Executor中运行并通过它们的Future 来取消任务那么可以设置mayInterruptIfRunning。当尝试取消某个任务时不宜直接中断线程池因为你并不知道当中断请求到达时正在运行什么任务——只能通过任务的Future 来实现取消。这也是在编写任务时要将中断视为一个取消请求的另一个理由可以通过任务的Future来取消它们。 程序清单7-10 给出了另一个版本的timedRun:将任务提交给一个ExecutorService,并通过一个定时的Future. get 来获得结果。如果get 在返回时抛出了一个TimeoutException,那么任务将通过它的Future来取消。(为了简化代码这个版本的timedRun在finally 块中将直接调用Future. cancel因为取消一个已完成的任务不会带来任何影响。)如果任务在被取消前就抛出一个异常那么该异常将被重新抛出以便由调用者来处理异常。在程序清单7-10中还给出了另一种良好的编程习惯取消那些不再需要结果的任务。(在程序清单6-13和程序清单6-16 中使用了相同的技术。) 程序清单7-10通过 Future 来取消任务                         public static void timedRun(Runnable r, long timeout,TimeUnit unit) throws InterruptedException { Future?task taskExec. submit®; try { task. get(timeout, unit); }catch (Timeout Exception e){ //   接下来任务将被取消 }catch (ExecutionException e){ ∥如果在任务中抛出了异常那么重新抛出该异常 throw launderThrowable(e. getCause()); }finally { //如果任务已经结束那么执行取消操作也不会带来任何影响 task. cancel(true);//如果任务正在运行,那么将被中断 } } 当Future. get 抛出InterruptedException或TimeoutException时,如果你知道不再需要结果,那么就可以调用Future. cancel来取消任务。 处理不可中断的阻塞 在Java 库中,许多可阻塞的方法都是通过提前返回或者抛出InterruptedException来响应中断请求的从而使开发人员更容易构建出能响应取消请求的任务。然而并非所有的可阻塞方法或者阻塞机制都能响应中断如果一个线程由于执行同步的Socket I/O.或者等待获得内置锁而阻塞那么中断请求只能设置线程的中断状态除此之外没有其他任何作用。对于那些由于执行不可中断操作而被阻塞的线程可以使用类似于中断的手段来停止这些线程但这要求我们必须知道线程阻塞的原因。 Java. io 包中的同步Socket I/O。在服务器应用程序中最常见的阻塞. I/O形式就是对套接字进行读取和写入。虽然InputStream和OutputStream中的read 和write 等方法都不会响应中断但通过关闭底层的套接字可以使得由于执行read 或write等方法而被阻塞的线程抛出一个SocketException。 Java. io包中的同步I/O。当中断一个正在InterruptibleChannel上等待的线程时,将抛出ClosedByInterruptException 并关闭链路(这还会使得其他在这条链路上阻塞的线程同样抛出ClosedByInterruptException)。当关闭一个InterruptibleChannel时,将导致所有在链路操作上阻塞的线程都抛出AsynchronousCloseException。大多数标准的Channel 都实现了InterruptibleChannel。 Selector 的异步I/O。如果一个线程在调用Selector. select 方法(在java. nio. channels中)时阻塞了,那么调用close 或wakeup 方法会使线程抛出ClosedSelectorException 并提前返回。 获取某个锁。如果一个线程由于等待某个内置锁而阻塞那么将无法响应中断因为线程认为它肯定会获得锁所以将不会理会中断请求。但是在Lock 类中提供了lockInterruptibly 方法该方法允许在等待一个锁的同时仍能响应中断请参见第13章。 程序清单7-11的ReaderThread 给出了如何封装非标准的取消操作。ReaderThread管理了一个套接字连接它采用同步方式从该套接字中读取数据并将接收到的数据传递给processBuffer。为了结束某个用户的连接或者关闭服务器,ReaderThread 改写了interrupt方法,使其既能处理标准的中断也能关闭底层的套接字。因此无论ReaderThread 线程是在read 方法中阻塞还是在某个可中断的阻塞方法中阻塞都可以被中断并停止执行当前的工作。 public class ReaderThread extends Thread { private final Socket socket; private final InputStream in; public ReaderThread(Socket socket) throws IOException { this. socket socket; this. in socket. get InputStream(); } public void interrupt(){ try { socket. close(); } catch (IOException ignored){} finally { super. interrupt(); } } public void run().{ try { byte[]buffnew byte[BUFSZ]; while (true){ int count in. read(buff); if  (count   0) break; else if (count 0) processBuffer(buff, count); } }   catch    (IOException e)   {/允许线程退出   /      } } } 采用newTaskFor来封装非标准的取消 我们可以通过newTaskFor方法来进一步优化ReaderThread中封装非标准取消的技术这 是Java 6在ThreadPoolExecutor中的新增功能。当把一个Callable 提交给ExecutorService时,submit 方法会返回一个Future,我们可以通过这个Future 来取消任务。newTaskFor是一个工厂方法,它将创建Future来代表任务。newTaskFor 还能返回一个RunnableFuture 接口,该接口扩展了Future和Runnable(并由FutureTask实现)。 通过定制表示任务的Future可以改变Future. cancel的行为。例如定制的取消代码可以实现日志记录或者收集取消操作的统计信息以及取消一些不响应中断的操作。通过改写interrupt方法,ReaderThread 可以取消基于套接字的线程。同样,通过改写任务的Future.cancel方法也可以实现类似的功能。 在程序清单7-12 的CancellableTask中定义了一个CancellableTask接口,该接口扩展了Callable,并增加了一个cancel 方法和一个newTask工厂方法来构造RunnableFuture。CancellingExecutor 扩展了ThreadPoolExecutor,并通过改写newTaskFor 使得CancellableTask可以创建自己的Future。 public interface CancellableTaskTextends CallableT{ void cancel(); RunnableFutureTnewTask(); } ThreadSafe public class CancellingExecutor extends ThreadPoolExecutor { … protectedTRunnableFutureTnewTaskFor(CallableTcallable){ if (callable instanceof CancellableTask) return ((CancellableTaskT) callable). newTask(); else return super. newTaskFor(callable); } } public abstract class SocketUsingTaskT implements CancellableTaskT{ GuardedBy(this) private socket socket; protected synchronized void setSocket(Socket s){socket s;} public synchronized void cancel(){ try if (socket !null) socket. close(); }catch (IOException ignored){} } public RunnableFutureTnewTask(){ return new FutureTaskT(this){ public boolean cancel(boolean mayInterruptIfRunning){ try { SocketUsingTask. this. cancel(); }finally {。 return super. cancel(mayInterruptIfRunning); 1 SocketUsingTask实现了CancellableTask,并定义了Future. cancel来关闭套接字和调用super. cancel。如果SocketUsingTask 通过其自己的Future来取消,那么底层的套接字将被关闭并且线程将被中断。因此它提高了任务对取消操作的响应性不仅能够在调用可中断方法的同时确保响应取消操作而且还能调用可阻调的套接字I/O 方法。