`
小野bupt
  • 浏览: 14038 次
  • 性别: Icon_minigender_1
文章分类
社区版块
存档分类
最新评论

有关线程中断和线程阻塞

 
阅读更多

一个线程都要从运行到结束都要经过3个阶段:

1、正在运行

2、准备结束运行

3、结束运行

那么怎么结束这个线程呢?可以通过下面这三个方法结束一个线程。

1、使用stop()方法强制结束线程。

2、使用thread.interrupt()方法发送中断。

3、在Thread对象中设置共享变量,通过在run方法中不断检测该变量的值来决定是否结束。

第一种方法,stop()方法

臭名昭著的stop()停止线程的方法已不提倡使用了,原因是什么呢?当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,假如一个线程正在执行:

synchronized void {
 x = 3;
 y = 4;
}
由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整

的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记 线程的stop方法,以后我们再也不要说“停止线程”了。


第二种方法,中断线程。

每个线程都有一个线程中断标志位来标志该线程是否被中断。我们可以通过检测该标志位是否为true来判断该线程是否被中断。调用线程的thread.interrupt()方法,将会设置该线程为中断状态,即设置为true。线程中断后的结果是死亡、还是等待新的任务或是继续运行至下一步,取决于这个程序本身线程会不时地检测这个中断标识位,以判断线程是否应该被中断(中断标识值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

当我们调用thread.interrupt()方法时,会将thread的中断标志位设为true。通过isInterrupted()方法可以判断标志位是否为true。此后程序是该继续还是结束就取决于程序本身了。

注意如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标识时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait、1.5中的condition.await及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标别位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。一个抛出了InterruptedException的线程的状态马上就会被置为非中断状态,如果catch语句没有处理异常,则下一 次循环中isInterrupted()为false,线程会继续执行,可能你N次抛出异常,也无法让线程停止。例如下面例子:

public class ThreadA extends Thread {
   int count=0;
   public void run(){
       System.out.println(getName()+"将要运行...");
       while(!this.isInterrupted()){
           System.out.println(getName()+"运行中"+count++);
           try{
               Thread.sleep(400);
           }catch(InterruptedException e){
               System.out.println(getName()+"从阻塞中退出...");
               System.out.println("this.isInterrupted()="+this.isInterrupted());
           }
       }
       System.out.println(getName()+"已经终止!");
   }
}
因为上例中try在while循环里面,当检测到异常时会执行打印语句,当再次进行while(!this.isInterrupted())判定的时候,请注意,由于此时中断标志位已经被重置为false了,所以while循环会一直继续下去。

public class ThreadA extends Thread {
   int count=0;
   public void run(){
       System.out.println(getName()+"将要运行...");
       while(!this.isInterrupted()){
           System.out.println(getName()+"运行中"+count++);
           try{
               Thread.sleep(400);
           }catch(InterruptedException e){
	       this.interrupt();
               System.out.println(getName()+"从阻塞中退出...");
               System.out.println("this.isInterrupted()="+this.isInterrupted());
           }
       }
       System.out.println(getName()+"已经终止!");
   }
}

比较良好处理方法是:

public void run() {   
    try {   
        /*  
         * 不管循环里是否调用过线程阻塞的方法如sleep、join、wait,这里还是需要加上  
         * !Thread.currentThread().isInterrupted()条件,虽然抛出异常后退出了循环,显  
         * 得用阻塞的情况下是多余的,但如果调用了阻塞方法但没有阻塞时,这样会更安全、更及时。  
         */  
        while (!Thread.currentThread().isInterrupted()&& more work to do) {   
            do more work    
        }   
    } catch (InterruptedException e) {   
        //线程在wait或sleep期间被中断了   
    } finally {   
        //线程结束前做一些清理工作   
    }   
}

注,synchronized在获锁的过程中是不能被中断的,意思是说如果产生了死锁,则不可能被中断(请参考后面的测试例子)。与synchronized功能相似的reentrantLock.lock()方法也是一样,它也不可中断的,即如果发生死锁,那么reentrantLock.lock()方法无法终止,如果调用时被阻塞,则它一直阻塞到它获取到锁为止。但是如果调用带超时的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那么如果线程在等待时被中断,将抛出一个InterruptedException异常,这是一个非常有用的特性,因为它允许程序打破死锁。你也可以调用reentrantLock.lockInterruptibly()方法,它就相当于一个超时设为无限的tryLock方法。

另外不要在你的底层代码里捕获InterruptedException异常后不处理,会处理不当,如下:

void mySubTask(){   
    ...   
    try{   
        sleep(delay);   
    }catch(InterruptedException e){}//不要这样做   
    ...   
}

如果你不知道抛InterruptedException异常后如何处理,那么你有如下好的建议处理方式:
1、在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标示会被清除),让外界通过判断Thread.currentThread().isInterrupted()标示来决定是否终止线程还是继续下去,应该这样做:

void mySubTask() {   
    ...   
    try {   
        sleep(delay);   
    } catch (InterruptedException e) {   
        Thread.currentThread().interrupted();   
    }   
    ...   
}

2、或者,更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出

void mySubTask() throws InterruptedException {   
    ...   
    sleep(delay);   
    ...   
} 

第三个方法,设置中断信号量。

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

package com.ljq.test;

public class ThreadTest extends Thread{
    //线程中断信号量
    volatile boolean stop=false; 
    
    public static void main(String[] args) throws Exception {
        ThreadTest thread=new ThreadTest();
        System.out.println("Starting thread...");   
        thread.start();   
        Thread.sleep(3000);   
        System.out.println("Asking thread to stop...");   
        // 设置中断信号量   
        thread.stop = true;   
        Thread.sleep(3000);   
        System.out.println("Stopping application...");  
    }
    
    
    @Override
    public void run() {
        //每隔一秒检测一下中断信号量
        while(!stop){
            System.out.println("Thread is running!");
            long begin=System.currentTimeMillis();
            /**
             * 使用while循环模拟sleep方法,这里不要使用sleep,否则在阻塞时会抛InterruptedException异常而退出循环,
             * 这样while检测stop条件就不会执行,失去了意义。 
             */
            while ((System.currentTimeMillis() - begin < 1000)) {
                
            }   
        }
        System.out.println("Thread exiting under request!");   
    }
}

注意:这里将stop变量定义为volatile类型,防止由于本地缓存产生错误。

参考文章:

http://www.cnblogs.com/linjiqin/archive/2011/04/11/2012695.html

http://blog.csdn.net/oscar999/article/details/1755759



分享到:
评论

相关推荐

    java线程阻塞中断与LockSupport使用介绍

    本文将详细介绍java线程阻塞中断和LockSupport的使用,需要了解更多的朋友可以参考下

    Java-并发-Java线程中断与停止线程详解

    线程中断   Java 中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。即“线程中断”并不是字面意思——线程真的中断了,而是设置了...

    浅析java线程中断的办法

    中断线程相关的方法 中断线程有一些相应的方法,这里列出来一下。 注意,如果是Thread.method(),则代表是静态方法。如果是thread.method()则代表着是类方法 void thread.stop() 这个方法能中断正在运行的线程,...

    线程生命周期.pdf

    以图表形式详细列出java线程生命周期,创建线程,线程就绪,线程阻塞,线程中断,线程唤醒,线程运行之间的关系

    java线程和容器专题课程第一部分

    java的线程机制是抢占式的,这表示调试 机制会周期性的中断线程,将上下文切换到另一个线程,从而为每个线程都提供时间片,使得每个线程都分分配到数据合理的时间去驱动 它的任务。在多线程中,一般对于static用的...

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    详细介绍java并发编程相关知识: 基础知识   并发与并行   Java并发演进历史   Java并发模型   线程模型   存储模型 JVM同步原语 volatile CAS 线程安全 ...  线程的中断与任务的取消   其他

    线程中断的方法以及静态方法isInterrupted和实例方法interrupted的区别

    线程中断 常见的有以下两种方式: 通过共享的标记来进行沟通 调用 interrupt() 方法来通知 通过共享的标记来实现中断 就是创建一个boolean类型的变量来控制循环是否进行,就是一个标记。 代码如下: /** * 描述:...

    一文带你读懂线程的启动和终止,

    3.1运行态的中断/阻塞态中断 3.2等待态的中断/超时等待态的中断 4、线程的终止 线程的启动和终止 不熟悉线程基本概念的同学,可以先看看我的上一篇文章拜托,学习并发编程之前请学习下线程! 1、线程的构造 在运行...

    线程运行状态

    线程是不太可能一直运行的,系统资源有限,多线程或多进程都必须在执行一段事件之后让出资源,交由其他线程继续执行。这是并发编程的本质。 这里有五种运行状态: ...4、阻塞:当某一个线程不再执行(中断,休眠,调

    基础技术部牛路《Java多线程入阶分享》纯干货

    1.使用线程的经验:设置名称、响应中断、使用ThreadLocal 2.Executor:ExecutorService和Future 3.阻塞队列:put和take、offer和poll、drainTo 4.线程间通信:lock、condition、wait、notify、notifyAll 5.Lock-free...

    STM32 CubeMX FreeRtos系统 基于lwRB通用环形缓冲区的串口非阻塞发送

    STM32工具 CubeMX 使用FreeRtos系统 基于lwRB通用环形缓冲区的串口非阻塞发送,程序使用printf,通过重定向fputc函数,将发送数据保存在FIFO中,可以在中断中调用printf,保证了系统的线程安全和中断安全,将发送...

    linux线程切换和进程切换的方法

    对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。 切换的性能消耗: 1、线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存...

    多线程编程指南PDF

    结合线程和RPC(远程过程调用) ...............................................................................................18 多线程概念.................................................................

    多线程基础

    文章目录多线程的创建和使用一、线程的创建方式二、线程的使用三、线程中断 一、线程的创建方式 创建线程常用的有两种方式 1,继承Thread类来创建线程类。 2,实现 Runnable 接口 。 创建线程-方法1-继承 Thread类 ...

    多线程编程指南(系统描述了线程标准 线程同步 多线程编程原则 等)

    结合线程和RPC(远程过程调用)18 多线程概念18 并发性和并行性 18 多线程结构一览 18 线程调度19 线程取消19 线程同步20 使用64 位体系结构20 2 基本线程编程23 线程库..............................................

    Android多线程断点续传下载示例详解

    多线程下载中,可以将下载这个耗时的操作放在子线程中执行,即不阻塞主线程,又符合Android开发的设计规范。 但是当下载的过程当中突然出现手机卡死,或者网络中断,手机电量不足关机的现象,这时,当手机可以正常...

    INDY控件使用指南

    TIdAntiFreeze在Indy内部定时中断对栈的调用,并在中断期间调用Application.ProcessMessages方法处理消息,而外部的Indy调用继续保存阻塞状态,就好像TIdAntiFreeze对象不存在一样。你只要在程序中的任意地方添加一...

    balsa-scgi:一个简单的 SCGI 侦听器,一个简单的线程阻塞 IO SCGI 服务器,可能对其他人有用

    轻木SCGI 一个简单的 SCGI 侦听器,一个简单的线程阻塞 IO SCGI 服务器,可能对其他人有用。执照Balsa SCGI 版权所有 (c) 2012,Chris Ellis 保留所有权利。 如果满足以下条件,则允许以源代码和二进制形式重新分发...

    Interrupt方法结束线程

    方法结束线程使用interrupt()方法来中断线程有两种情况: 1. 线程处于阻塞状态:如使用了sleep,同步锁的wait,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出...

    JAVA2核心技术(中文的PDF).part3.rar

    1.2 中断线程 11 1.3 线程状态 13 1.3.1 新生线程 13 1.3.2 可运行线程 13 1.3.3 被阻塞线程 14 1.3.4 死线程 15 1.4 线程属性 15 1.4.1 线程优先级 15 1.4.2 守护线程 16 1.4.3 线程组 16 1.4.4 未捕获异常处理器 ...

Global site tag (gtag.js) - Google Analytics