文档章节

7.1停止Java线程,小心interrupt()方法

s
 sxl01890
发布于 2016/06/22 16:29
字数 2139
阅读 49
收藏 6
点赞 0
评论 0

程序是很简易的。然而,在编程人员面前,多线程呈现出了一组新的难题,如果没有被恰当的解决,将导致意外的行为以及细微的、难以发现的错误。
  在本篇文章中,我们针对这些难题之一:如何中断一个正在运行的线程。 

背景 
    中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序。虽然初次看来它可能显得简单,但是,你必须进行一些预警以实现期望的结果。你最好还是牢记以下的几点告诫。 

    首先,忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程,然而,这种方法是不安全也是不受提倡的,这意味着,在未来的Java版本中,它将不复存在。 

    一些轻率的家伙可能被另一种方法Thread.interrupt所迷惑。尽管,其名称似乎在暗示着什么,然而,这种方法并不会中断一个正在运行的线程(待会将进一步说明),正如Listing A中描述的那样。它创建了一个线程,并且试图使用Thread.interrupt方法停止该线程。Thread.sleep()方法的调用,为线程的初始化和中止提供了充裕的时间。线程本身并不参与任何有用的操作。 
Listing A 

 

[java] view plain copy

  1. class Example1 extends Thread {  
  2.             boolean stop=false;  
  3.             public static void main( String args[] ) throws Exception {  
  4.             Example1 thread = new Example1();  
  5.             System.out.println( "Starting thread..." );  
  6.             thread.start();  
  7.             Thread.sleep( 3000 );  
  8.             System.out.println( "Interrupting thread..." );  
  9.             thread.interrupt();  
  10.             Thread.sleep( 3000 );  
  11.             System.out.println("Stopping application..." );  
  12.             //System.exit(0);  
  13.             }  
  14.             public void run() {  
  15.             while(!stop){  
  16.             System.out.println( "Thread is running..." );  
  17.             long time = System.currentTimeMillis();  
  18.             while((System.currentTimeMillis()-time < 1000)) {  
  19.             }  
  20.             }  
  21.             System.out.println("Thread exiting under request..." );  
  22.             }  
  23.             }  

如果你运行了Listing A中的代码,你将在控制台看到以下输出: 

Starting thread... 

Thread is running... 

Thread is running... 

Thread is running... 

Interrupting thread... 

Thread is running... 

Thread is running... 

Thread is running... 

Stopping application... 

Thread is running... 

Thread is running... 

Thread is running... 
............................... 
甚至,在Thread.interrupt()被调用后,线程仍然继续运行。 

真正地中断一个线程 

    中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。Listing B描述了这一方式。 

Listing B 

[java] view plain copy

  1. class Example2 extends Thread {  
  2.   volatile boolean stop = false;  
  3.   public static void main( String args[] ) throws Exception {  
  4.    Example2 thread = new Example2();  
  5.    System.out.println( "Starting thread..." );  
  6.    thread.start();  
  7.    Thread.sleep( 3000 );  
  8.    System.out.println( "Asking thread to stop..." );  
  9.   
  10.    thread.stop = true;  
  11.    Thread.sleep( 3000 );  
  12.    System.out.println( "Stopping application..." );  
  13.    //System.exit( 0 );  
  14.   }  
  15.   
  16.   public void run() {  
  17.     while ( !stop ) {  
  18.      System.out.println( "Thread is running..." );  
  19.       long time = System.currentTimeMillis();  
  20.       while ( (System.currentTimeMillis()-time < 1000) && (!stop) ) {  
  21.       }  
  22.     }  
  23.    System.out.println( "Thread exiting under request..." );  
  24.   }  
  25. }  


运行Listing B中的代码将产生如下输出(注意线程是如何有秩序的退出的) 

Starting thread... 

Thread is running... 

Thread is running... 

Thread is running... 

Asking thread to stop... 

Thread exiting under request... 

Stopping application... 

   虽然该方法要求一些编码,但并不难实现。同时,它给予线程机会进行必要的清理工作,这在任何一个多线程应用程序中都是绝对需要的。请确认将共享变量定义成volatile 类型或将对它的一切访问封入同步的块/方法(synchronized blocks/methods)中。 

到目前为止一切顺利!但是,当线程等待某些事件发生而被阻塞,又会发生什么?当然,如果线程被阻塞,它便不能核查共享变量,也就不能停止。这在许多情况下会发生,例如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()时,这里仅举出一些。 

他们都可能永久的阻塞线程。即使发生超时,在超时期满之前持续等待也是不可行和不适当的,所以,要使用某种机制使得线程更早地退出被阻塞的状态。 

很不幸运,不存在这样一种机制对所有的情况都适用,但是,根据情况不同却可以使用特定的技术。在下面的环节,我将解答一下最普遍的例子。 

使用Thread.interrupt()中断线程 

  正如Listing A中所描述的,Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。 

    因此,如果线程被上述几种方法阻塞,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。Listing C这个示例描述了该技术。 

Listing C 

 

 

[java] view plain copy

  1. class Example3 extends Thread {  
  2.   volatile boolean stop = false;  
  3.   public static void main( String args[] ) throws Exception {  
  4.    Example3 thread = new Example3();  
  5.    System.out.println( "Starting thread..." );  
  6.    thread.start();  
  7.    Thread.sleep( 3000 );  
  8.    System.out.println( "Asking thread to stop..." );  
  9.    thread.stop = true;//如果线程阻塞,将不会检查此变量  
  10.    thread.interrupt();  
  11.    Thread.sleep( 3000 );  
  12.    System.out.println( "Stopping application..." );  
  13.    //System.exit( 0 );  
  14.   }  
  15.   
  16.   public void run() {  
  17.     while ( !stop ) {  
  18.      System.out.println( "Thread running..." );  
  19.       try {  
  20.       Thread.sleep( 1000 );  
  21.       } catch ( InterruptedException e ) {  
  22.       System.out.println( "Thread interrupted..." );  
  23.       }  
  24.     }  
  25.    System.out.println( "Thread exiting under request..." );  
  26.   }  
  27. }  


一旦Listing C中的Thread.interrupt()被调用,线程便收到一个异常,于是逃离了阻塞状态并确定应该停止。运行以上代码将得到下面的输出: 

Starting thread... 

Thread running... 

Thread running... 

Thread running... 

Asking thread to stop... 

Thread interrupted... 

Thread exiting under request... 

Stopping application... 


中断I/O操作 
    然而,如果线程在I/O操作进行时被阻塞,又会如何?I/O操作可以阻塞线程一段相当长的时间,特别是牵扯到网络应用时。例如,服务器可能需要等待一个请求(request),又或者,一个网络应用程序可能要等待远端主机的响应。 

如果你正使用通道(channels)(这是在Java 1.4中引入的新的I/O API),那么被阻塞的线程将收到一个ClosedByInterruptException异常。如果情况是这样,其代码的逻辑和第三个例子中的是一样的,只是异常不同而已。 

但是,你可能正使用Java1.0之前就存在的传统的I/O,而且要求更多的工作。既然这样,Thread.interrupt()将不起作用,因为线程将不会退出被阻塞状态。Listing D描述了这一行为。尽管interrupt()被调用,线程也不会退出被阻塞状态 

Listing D 

 

 

[java] view plain copy

  1. import java.io.*;  
  2. class Example4 extends Thread {  
  3.   public static void main( String args[] ) throws Exception {  
  4.     Example4 thread = new Example4();  
  5.    System.out.println( "Starting thread..." );  
  6.    thread.start();  
  7.    Thread.sleep( 3000 );  
  8.    System.out.println( "Interrupting thread..." );  
  9.    thread.interrupt();  
  10.    Thread.sleep( 3000 );  
  11.    System.out.println( "Stopping application..." );  
  12.    //System.exit( 0 );  
  13.   }  
  14.   
  15.   public void run() {  
  16.    ServerSocket socket;  
  17.     try {  
  18.       socket = new ServerSocket(7856);  
  19.     } catch ( IOException e ) {  
  20.      System.out.println( "Could not create the socket..." );  
  21.       return;  
  22.     }  
  23.     while ( true ) {  
  24.      System.out.println( "Waiting for connection..." );  
  25.       try {  
  26.        Socket sock = socket.accept();  
  27.       } catch ( IOException e ) {  
  28.       System.out.println( "accept() failed or interrupted..." );  
  29.       }  
  30.     }  
  31.   }  
  32. }  


 很幸运,Java平台为这种情形提供了一项解决方案,即调用阻塞该线程的套接字的close()方法。在这种情形下,如果线程被I/O操作阻塞,该线程将接收到一个SocketException异常,这与使用interrupt()方法引起一个InterruptedException异常被抛出非常相似。 

唯一要说明的是,必须存在socket的引用(reference),只有这样close()方法才能被调用。这意味着socket对象必须被共享。Listing E描述了这一情形。运行逻辑和以前的示例是相同的。 

Listing E 

 

 

[java] view plain copy

  1. import java.net.*;  
  2. import java.io.*;  
  3. class Example5 extends Thread {  
  4.   volatile boolean stop = false;  
  5.   volatile ServerSocket socket;  
  6.   public static void main( String args[] ) throws Exception {  
  7.     Example5 thread = new Example5();  
  8.    System.out.println( "Starting thread..." );  
  9.    thread.start();  
  10.    Thread.sleep( 3000 );  
  11.    System.out.println( "Asking thread to stop..." );  
  12.    thread.stop = true;  
  13.    thread.socket.close();  
  14.    Thread.sleep( 3000 );  
  15.    System.out.println( "Stopping application..." );  
  16.    //System.exit( 0 );  
  17.   }  
  18.   public void run() {  
  19.     try {  
  20.       socket = new ServerSocket(7856);  
  21.     } catch ( IOException e ) {  
  22.      System.out.println( "Could not create the socket..." );  
  23.       return;  
  24.     }  
  25.     while ( !stop ) {  
  26.      System.out.println( "Waiting for connection..." );  
  27.       try {  
  28.        Socket sock = socket.accept();  
  29.       } catch ( IOException e ) {  
  30.       System.out.println( "accept() failed or interrupted..." );  
  31.       }  
  32.     }  
  33.    System.out.println( "Thread exiting under request..." );  
  34.   }  
  35. }  


以下是运行Listing E中代码后的输出: 

Starting thread... 

Waiting for connection... 

Asking thread to stop... 

accept() failed or interrupted... 

Thread exiting under request... 

Stopping application... 

多线程是一个强大的工具,然而它正呈现出一系列难题。其中之一是如何中断一个正在运行的线程。如果恰当地实现,使用上述技术中断线程将比使用Java平台上已经提供的内嵌操作更为简单。

本文转载自:http://www.blogjava.net/jinfeng_wang/archive/2008/04/27/196477.html

共有 人打赏支持
s
粉丝 3
博文 34
码字总数 55191
作品 0
浦东
java基础thread——多线程的纷争(循序渐进)

一、多线程概述 进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。 多进程有什么意义呢? 单进程的计算机只能做一件事情,而我们现在...

潇潇漓燃 ⋅ 05/30 ⋅ 0

Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀 ⋅ 04/16 ⋅ 0

深入浅出Java多线程,看着篇就够了

Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。 多线程是多任务的一种特别的形...

java高级架构牛人 ⋅ 05/22 ⋅ 0

Spring-boot+Dubbo应用启停源码分析

背景介绍 Dubbo Spring Boot 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发。同时也整合了 Spring Boot 特性: 自动装配 (比如: 注解驱动, 自动装配等). Production-Ready (比...

ralf0131 ⋅ 05/29 ⋅ 0

Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

原文:https://blog.csdn.net/xionghan01/article/details/52840358 一、线程5种状态 新建状态(New) 新创建了一个线程对象。 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的...

Tenderrain ⋅ 06/22 ⋅ 0

Java内存模型之happens-before和重排序

重排序 在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,满足以下两个条件: 在单线程环境下不能改变程序运行的结果; 存在数据依赖关系的不允许重排序 happens - befo...

细节探索者 ⋅ 05/02 ⋅ 0

Java 使用 happen-before 规则实现共享变量的同步操作

前言 熟悉 Java 并发编程的都知道,JMM(Java 内存模型) 中的 happen-before(简称 hb)规则,该规则定义了 Java 多线程操作的有序性和可见性,防止了编译器重排序对程序结果的影响。按照官方的...

stateIs0 ⋅ 01/20 ⋅ 0

Java并发系列 1--线程基础与synchronized关键字

程序开发中并发的场景还是比较常见的,特别是当下分布式环环境开发大行其道的情况下,从前端处理,到服务调用、缓存处理、数据库处理、文件处理、消息处理等等,无不需要并发的知识。 从今天...

大大枣 ⋅ 05/31 ⋅ 0

JVM(Thread/Stack)

JVM Thread/Stack Memory Size JVM Thread/Stack Object states (6 states) Dump OS Thread/Stack OS的线程运行状态 Iuput(top): Output: or input(ps): Dump Thread/Stack Analysis 注意thr......

赵-猛 ⋅ 2016/10/12 ⋅ 0

Java学习笔记--线程和多线程线程池(简单理解)

线程: 单核的cpu在一个时间片中只能执行一个应用程序 各个程序其实在做cpu的资源真多战而已 cpu做了快速的切换动作 疑问 :线程负责了代码 的执行,我们之前没有学过线程,为什么代码可以执...

codingcoge ⋅ 05/02 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

JEPLUS主从功能配置之主从布局的设置——JEPLUS软件快速开发平台

JEPLUS主从功能配置之主从布局的设置 主从功能配置成功之后就需要根据业务需求来调整主从功能的数据显示方式,不同的主从数据的显示可以达到不同的主从数据显示效果,今天这篇笔记就讲解一下...

JEPLUS ⋅ 2分钟前 ⋅ 0

如何利用极光推送的新功能玩转世界杯营销

四年一次的世界杯已经于6月14日开赛!对于app的运营人员而言,这场远在俄罗斯的绿茵征战绝不仅仅牵动着球迷们的心,更拨动着众多互联网企业运营人员的神经。在这场营销大战中,push显然是app...

极光推送 ⋅ 6分钟前 ⋅ 0

Spring Cloud构建微服务架构-Hystrix依赖隔离

依赖隔离 “舱壁模式”对于熟悉Docker的读者一定不陌生,Docker通过“舱壁模式”实现进程的隔离,使得容器与容器之间不会互相影响。而Hystrix则使用该模式实现线程池的隔离,它会为每一个Hys...

itcloud ⋅ 8分钟前 ⋅ 0

SpringCloud 微服务 (八) 统一配置中心 Config Server&Client

壹 Spring Cloud Config 统一配置中心,方便维护配置文件,对一些公司对数据库密码等敏感的信息,对普通开发人员不公开,放在运维人员手上,对配置作一个隔离作用,另外项目线上的配置改动都要重新...

___大侠 ⋅ 12分钟前 ⋅ 0

echarts轮播地图并结合鼠标浮动点击

直接上代码 timeId=setInterval(function () { if(count<11){ myChart.dispatchAction({ type: 'downplay', ......

莫西摩西 ⋅ 15分钟前 ⋅ 0

基于 HTML5 的工业互联网 3D 可视化应用

工业企业中生产线处于高速运转,由工业设备所产生、采集和处理的数据量远大于企业中计算机和人工产生的数据,生产线的高速运转则对数据的实时性要求也更高。破解这些大数据就是企业在新一轮制...

xhload3d ⋅ 17分钟前 ⋅ 0

Nging启动与停止bat

start_nginx.bat @echo off  f:  cd F:\server\nginx-1.13.6  echo "nginx is starting on port 80"  start "" "nginx.exe"  exit   stop_nginx.bat @echo off::windows ......

Jay丶 ⋅ 19分钟前 ⋅ 0

SuRF: 一个优化的 Fast Succinct Tries

作者:唐刘 在前一篇文章中,我简单介绍了 Succinct Data Structure,这里我们继续介绍 SuRF。 Fast Succinct Tries SuRF 的核心数据结构就是 Fast Succinct Tries(FST),一种空间节省,支...

TiDB ⋅ 23分钟前 ⋅ 0

Kubernetes(六) - Secret和私有仓库认证

对一个公司来说安全也是最为重要的因为可能一旦出现安全问题可能这个公司就完了,所以对密码管理是一个长久不变的话题,Kubernetes对密码管理提供了Secret组件进行管理,最终映射成环境变量,文件...

喵了_个咪 ⋅ 25分钟前 ⋅ 0

DevOps的三大原则

DevOps的出现有其必然性。在软件开发生命周期中,遇到了两次瓶颈。第一次瓶颈是在需求阶段和开发阶段之间,针对不断变化的需求,对软件开发者提出了高要求,后来出现了敏捷方法论,强调适应需...

inidcard ⋅ 25分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部