您的位置:首页 > 其它

interrupt、isinterrupt、interrupted的理解和使用

2014-07-29 11:59 477 查看
在java的线程Thread类中有三个方法,比较容易混淆,在这里解释一下

(1)interrupt:置线程的中断状态

(2)isInterrupt:线程是否中断

(3)interrupted:返回线程的上次的中断状态,并清除中断状态

举个例子:

class MyThread extends Thread {

......

......

public void run() {

try {

while(!Thread.currentThread().isInterrupted()) {

//当达到队列容量时,在这里会阻塞

//put的内部会调用LockSupport.park()这个是用来阻塞线程的方法

//当其他线程,调用此线程的interrupt()方法时,会设置一个中断标志

//LockSupport.part()中检测到这个中断标志,会抛出InterruptedException,并清除线程的中断标志

//因此在异常段调用Thread.currentThread().isInterrupted()返回为false

ArrayBlockingQueue.put(somevalue);

}

} catch (InterruptedException e) {

//由于阻塞库函数,如:Object.wait,Thread.sleep除了抛出异常外,还会清除线程中断状态,因此可能在这里要保留线程的中断状态

Thread.currentThread().interrupt();

}

}

public void cancel() {

interrupt();

}

}

外部调用

MyThread thread = new MyThread();

thread.start();

......

thread.cancel();

thread.isInterrupted();

一般来说,阻塞函数,如:Thread.sleep、Thread.join、Object.wait、LockSupport.park等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态

对于InterruptedException的处理,可以有两种情况:

(1)外层代码可以处理这个异常,直接抛出这个异常即可

(2)如果不能抛出这个异常,比如在run()方法内,因为在得到这个异常的同时,线程的中断状态已经被清除了,需要保留线程的中断状态,则需要调用Thread.currentThread().interrupt()

另外,Thread.interrupted()在jdk库的源代码中比较常用,因为它既可以得到上一次线程的中断标志值,又可以同时清除线程的中断标志,一举两得,但同时也有坏处,就是这个函数有清除中断状态的副作用,不容易理解.转自:http://blog.csdn.net/gtuu0123/archive/2010/11/27/6040105.aspx

最近在学习Java线程相关的东西,和大家分享一下,有错误之处欢迎大家指正.

假如我们有一个任务如下,交给一个Java线程来执行,如何才能保证调用interrupt()来中断它呢?

Java代码



class ATask implements Runnable{

private double d = 0.0;

public void run() {

//死循环执行打印"I am running!" 和做消耗时间的浮点计算

while (true) {

System.out.println("I am running!");

for (int i = 0; i < 900000; i++) {

d = d + (Math.PI + Math.E) / d;

}

//给线程调度器可以切换到其它进程的信号

Thread.yield();

}

}

}

public class InterruptTaskTest {

public static void main(String[] args) throws Exception{

//将任务交给一个线程执行

Thread t = new Thread(new ATask());

t.start();

//运行一断时间中断线程

Thread.sleep(100);

System.out.println("****************************");

System.out.println("Interrupted Thread!");

System.out.println("****************************");

t.interrupt();

}

}

运行这个程序,我们发现调用interrupt()后,程序仍在运行,如果不强制结束,程序将一直运行下去,如下所示:

Java代码



......

I am running!

I am running!

I am running!

I am running!

****************************

Interrupted Thread!

****************************

I am running!

I am running!

I am running!

I am running!

I am running!

....

虽然中断发生了,但线程仍然在进行,离开线程有两种常用的方法:

抛出InterruptedException和用Thread.interrupted()检查是否发生中断,下面分别看一下这两种方法:

1.在阻塞操作时如Thread.sleep()时被中断会抛出InterruptedException(注意,进行不能中断的IO操作而阻塞和要获得对象的锁调用对象的synchronized方法而阻塞时不会抛出InterruptedException)

Java代码



class ATask implements Runnable{

private double d = 0.0;

public void run() {

//死循环执行打印"I am running!" 和做消耗时间的浮点计算

try {

while (true) {

System.out.println("I am running!");

for (int i = 0; i < 900000; i++) {

d = d + (Math.PI + Math.E) / d;

}

//休眠一断时间,中断时会抛出InterruptedException

Thread.sleep(50);

}

} catch (InterruptedException e) {

System.out.println("ATask.run() interrupted!");

}

}

}

程序运行结果如下:

Java代码



I am running!

I am running!

****************************

Interrupted Thread!

****************************

ATask.run() interrupted!

可以看到中断任务时让任务抛出InterruptedException来离开任务.

2.Thread.interrupted()检查是否发生中断.Thread.interrupted()能告诉你线程是否发生中断,并将清除中断状态标记,所以程序不会两次通知你线程发生了中断.

Java代码



class ATask implements Runnable{

private double d = 0.0;

public void run() {

//检查程序是否发生中断

while (!Thread.interrupted()) {

System.out.println("I am running!");

for (int i = 0; i < 900000; i++) {

d = d + (Math.PI + Math.E) / d;

}

}

System.out.println("ATask.run() interrupted!");

}

}

程序运行结果如下:

Java代码



I am running!

I am running!

I am running!

I am running!

I am running!

I am running!

I am running!

****************************

Interrupted Thread!

****************************

ATask.run() interrupted!

我们可结合使用两种方法来达到可以通过interrupt()中断线程.请看下面例子:

Java代码



class ATask implements Runnable{

private double d = 0.0;

public void run() {

try {

//检查程序是否发生中断

while (!Thread.interrupted()) {

System.out.println("I am running!");

//point1 before sleep

Thread.sleep(20);

//point2 after sleep

System.out.println("Calculating");

for (int i = 0; i < 900000; i++) {

d = d + (Math.PI + Math.E) / d;

}

}

} catch (InterruptedException e) {

System.out.println("Exiting by Exception");

}

System.out.println("ATask.run() interrupted!");

}

}

在point1之前处point2之后发生中断会产生两种不同的结果,可以通过修改InterruptTaskTest main()里的Thread.sleep()的时间来达到在point1之前产生中断或在point2之后产生中断.

如果在point1之前发生中断,程序会在调用Thread.sleep()时抛出InterruptedException从而结束线程.这和在Thread.sleep()时被中断是一样的效果.程序运行结果可能如下:

Java代码



I am running!

Calculating

I am running!

Calculating

I am running!

Calculating

I am running!

****************************

Interrupted Thread!

****************************

Exiting by Exception

ATask.run() interrupted!

如果在point2之后发生中断,线程会继续执行到下一次while判断中断状态时.程序运行结果可能如下:

Java代码



I am running!

Calculating

I am running!

Calculating

I am running!

Calculating

****************************

Interrupted Thread!

****************************

ATask.run() interrupted!

在学校的论坛Java版发现很多问关于这样的问题,比如这几个方法有什么区别,想看t.interrupt()方法后线程的中断状态;如何终止一个线程

其实之前已经大部分提及到。现总结一下,然后加上例子,毕竟例子容易理解
http://www.blogjava.net/fhtdy2004/archive/2009/06/08/280728.html中有关interrupt()的解释已经很清楚了
interruptpublic void interrupt()

中断线程。

如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。

如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。

如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。

如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。

如果以前的条件都没有保存,则该线程的中断状态将被设置。

抛出:

SecurityException - 如果当前线程无法修改该线程

--------------------------------------------------------------------------------

interruptedpublic static boolean interrupted()

测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

返回:

如果当前线程已经中断,则返回 true;否则返回 false。

另请参见:

isInterrupted()

--------------------------------------------------------------------------------

isInterruptedpublic boolean isInterrupted()

测试线程是否已经中断。线程的中断状态 不受该方法的影响。

返回:

如果该线程已经中断,则返回 true;否则返回 false。

另请参见:

interrupted()

t.interrupt()不会中断正在执行的线程,只是将线程的标志位设置成true。但是如果线程在调用sleep(),join(),wait()方法时线程被中断,则这些方法会抛出InterruptedException,在catch块中捕获到这个异常时,线程的中断标志位已经被设置成false了,因此在此catch块中调用t.isInterrupted(),Thread.interrupted()始终都为false,

而t.isInterrupted与Thread.interrupted()的区别是API中已经说明很明显了,Thread.interrupted()假如当前的中断标志为true,则调完后会将中断标志位设置成false

Java代码



package threadtest;

import java.util.Timer;

import java.util.TimerTask;

class CanStop extends Thread {

private int counter = 0;

public void run() {

boolean done = false;

try{

Thread.sleep(100);//设置成100比主线程中的500要小

}catch(InterruptedException ie){

ie.printStackTrace();

//return;假如要使用interrupt来终止线程则在捕获的InterruptedException中return

}

while (counter < 100000 &&!done) {

System.out.println(counter++);

//在主线程中调用stoppable.interrupt()之前为false,假如之后没有调用Thread.interrupted()则一直为true,

//否则为第一次为true,调用Thread.interrupted之后为false

System.out.println("in thread stoppable.isInterrupted() "+isInterrupted());

//System.out.println("stoppable.isInterrupted() "+Thread.interrupted());////在主线程中调用stoppable.interrupt()之前为false,之后只有第一个会显示为true,之后全为false

//调用Thread.interrupted()一次会清除线程的中断标志位,因此以后都为false

if(Thread.interrupted()==true){

try{

//Thread.interrupted()会清除中断标志位,显然这里面只会调用一次

System.out.println("in thread after Thread.interrupted() "+isInterrupted());

sleep(10000);

}catch(InterruptedException ie){

ie.printStackTrace();

}

}

}

}

}

public class CheckInterrupt {

public static void main(String[] args) {

final CanStop stoppable = new CanStop();

stoppable.start();

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Requesting Interrupt");

stoppable.interrupt();//不会中断正在执行的线程,原因是因为interrupt()方法只设置中断状态标志位为true

System.out.println("in timer stoppable.isInterrupted() "+stoppable.isInterrupted());

}

}, 500); // run() after 500 milliseconds

}

}

2,关于interrupte()打断sleep()

package threadtest;

//Understanding join().

class Sleeper extends Thread {

private int duration;

public Sleeper(String name, int sleepTime) {

super(name);

duration = sleepTime;

start();

}

public void run() {

try {

sleep(duration);

} catch (InterruptedException e) {

// System.out.println(getName() + " was interrupted. " +

// "isInterrupted(): " + isInterrupted());

System.out.println(getName() + " in catch Thread.interrupted(). "

+ "Thread.interrupted(): " + Thread.interrupted());

return;

}

System.out.println(getName() + " has awakened");

}

}

class Joiner extends Thread {

private Sleeper sleeper;

public Joiner(String name, Sleeper sleeper) {

super(name);

this.sleeper = sleeper;

start();

}

public void run() {

try {

sleeper.join();

} catch (InterruptedException e) {

//run方法不能Throw CheckedException,要抛只能抛出RuntimeException,也不会被主线程捕获

//要使主线程能够捕获这个RuntimeException请参见另外一篇文章

//地址:http://www.blogjava.net/fhtdy2004/archive/2009/08/07/290210.html

throw new RuntimeException(e);

}

System.out.println(getName() + " join completed");

}

}

public class Joining {

public static void main(String[] args) {

Sleeper sleepy = new Sleeper("Sleepy", 1500),

grumpy = new Sleeper("Grumpy", 1500);

Joiner dopey = new Joiner("Dopey", sleepy),

doc = new Joiner("Doc",grumpy);

grumpy.interrupt();

//doc.interrupt();

}

}

Sleeper是一个会睡上一段时间的Thread,至于睡多长时间,这要由构造函数的参数决定。Sleeper的run( )的sleep( )可以因时限到期而返回,也可以被interrupt( )打断。catch语句在报告中断的同时,会一并报告isInterrupted( )。当有别的线程调用了本线程的interrupt( )时,会设置一个标记以表示这个这个线程被打断了。当本线程捕获这个异常的时候,会清除这个标志。所以catch语句会永远报告说isInterrupted( )是false。这个标记是用来应付其它情况的,或许在没出异常的情况下,线程要用它来检查自己是不是被中断了。

Joiner是另一个线程,它调用了Sleeper的join( ),所以它要等Sleeper醒过来。main( )创建了两个Sleeper分派给两个Joiner。你会发现,不论Sleeper是被打断还是正常结束,Joiner都会随Sleeper一道结束。

2,如何终止一个线程:

package test.thread.one;

import java.util.Timer;

import java.util.TimerTask;

class CanStop extends Thread {

// Must be volatile:

private volatile boolean stop = false;

private int counter = 0;

public void run() {

while (!stop && counter < 100000) {

System.out.println(counter++);

}

if (stop)

System.out.println("Detected stop");

}

public void requestStop() {

stop = true;

}

}

public class Stopping {

public static void main(String[] args) {

final CanStop stoppable = new CanStop();

stoppable.start();

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Requesting stop");

stoppable.requestStop();

}

}, 500); // run() after 500 milliseconds

}

}

stop必须是volatile的,这样才能确保run( )方法能看到它(否则它会使用本地的缓存值)。这个线程的"任务"是打印10,000个数字,所以当counter >= 10000或有人要它停下来的时候,它就结束了。注意requestStop( )不是synchronized,因为stop既是boolean(改成true是一个原子操作)又是volatile的。

或者

Java代码



package test.thread.three;

import java.util.Timer;

import java.util.TimerTask;

class CanStop extends Thread {

private boolean stop = false;

private int counter = 0;

public void run() {

boolean done = false;

try{

Thread.sleep(100);

}catch(InterruptedException ie){

ie.printStackTrace();

//return;假如要使用interrupt来终止线程则在捕获的InterruptedException中return

}

while (!getStopRequest() && counter < 100000 &&!done) {

System.out.println(counter++);

}

if (getStopRequest())

System.out.println("Detected stop");

}

public synchronized boolean getStopRequest(){

return stop;

}

public synchronized void requestStop() {

stop = true;

}

}

public class Stopping {

public static void main(String[] args) {

final CanStop stoppable = new CanStop();

stoppable.start();

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Requesting stop");

stoppable.requestStop();

}

}, 500); // run() after 500 milliseconds

}

}

打断受阻的线程

有时线程受阻之后就不能再做轮询了,比如在等输入,这时你就不能像前面那样去查询旗标了。碰到这种情况,你可以用Thread.interrupt( )方法打断受阻的线程:

//: c13:Interrupt.java

// Using interrupt() to break out of a blocked thread.

import java.util.*;

class Blocked extends Thread {

public Blocked() {

System.out.println("Starting Blocked");

start();

}

public void run() {

try {

synchronized(this) {

wait(); // Blocks

}

} catch(InterruptedException e) {

System.out.println("Interrupted");

}

System.out.println("Exiting run()");

}

}

public class Interrupt {

static Blocked blocked = new Blocked();

public static void main(String[] args) {

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Preparing to interrupt");

blocked.interrupt();

blocked = null; // to release it

}

}, 2000); // run() after 2000 milliseconds

}

} ///

3.避免过多的同步,永远不要在循环外面调用wait

为了避免死锁的危险,在一个被同步的的方法或者代码快中,永远不要放弃对客户的限制。

换句话说,在一个被同步的区域内部,不要调用一个可被改写的公有或受保护的方法(这样的方法往往是一个抽象方法,但偶尔他们也会有一个默认的实现,)从包含该同步区域的类的角度来看,这样的方法是一个外来者alien。这个类不知道该类会做什么事情,也控制不力它。客户可以为这个外来方法提供一个实现,并且在该方法中创建了一个线程,再回调到这个类中。然后,新建的线程试图获取原线程所拥有的那把锁,这样会导致新建的线程被阻塞。如果创建该线程的方法在等待这个线程完成这个任务,则死锁就形成了。

Object.wait方法的作用是使一个线程等待某个条件。它一定是在一个同步区域中被调用,而且该同步区域锁住了被调用的对象。下面是wait方法的标准模式:

Java代码



synchronized(obj){

while(<condition does not hold>)

obj.wait();

...//perform action appropriate to condition

}

总是使用wait循环模式来调用wait方法。而不是if来调用。永远不要在循环的外面调用wait。循环被用于等待的前后测试条件

Java代码



package effective.java;

import java.io.BufferedInputStream;

import java.util.LinkedList;

import java.util.List;

public abstract class WorkQueue {

private final List queue = new LinkedList();

private boolean stopped = false;

StringBuffer sb;

BufferedInputStream bis;

protected WorkQueue(){

new WorkerThread2().start();

}

public final void enqueue(Object workItem){

synchronized(queue){

queue.add(workItem);

queue.notify();

}

}

public final void stop(){

synchronized(queue){

stopped = true;

queue.notify();

}

}

protected abstract void processItem(Object workItem)throws InterruptedException;

//Broken - invokes alien method from synchronized block

private class WorkerThread extends Thread{

public void run(){

while(true){

synchronized(WorkQueue.this.queue){

try{

while(queue.isEmpty() && !stopped){

queue.wait();

}

}catch(InterruptedException ie){

ie.printStackTrace();

return;

}

if(stopped)

return;

Object workItem = queue.remove(0);

try{

processItem(workItem);//lock held

}catch(InterruptedException ie){

System.out.println("ddd"+ie);

return;

}

}

}

}

}

//Alien method outside synchronized block -"open call"

private class WorkerThread2 extends Thread{

public void run(){

while(true){

Object workItem = null;

synchronized(WorkQueue.this.queue){

try{

while(queue.isEmpty() && !stopped){

queue.wait();

}

}catch(InterruptedException ie){

return;

}

if(stopped)

return;

workItem = queue.remove(0);

}

try{

processItem(workItem);//No lock held

}catch(InterruptedException ie){

return;

}

}

}

}

}

package effective.java;

public class DisplayQueue extends WorkQueue {

@Override

protected void processItem(Object workItem) throws InterruptedException {

System.out.println(workItem);

System.out.println("模拟此线程做耗时工作");

Thread.sleep(1000);

}

public static void main(String[] args){

WorkQueue wq = new DisplayQueue();

for(int i=0;i<10;i++){

String s = new String("object_"+i);

System.out.println("main thread add " + s+" to queue");

wq.enqueue(s);

try{

Thread.sleep(500);

}catch(InterruptedException ie){

ie.printStackTrace();

}

}

//wq.stop();

}

}

class DeadLockQueue extends WorkQueue{

@Override

protected void processItem(final Object workItem) throws InterruptedException {

Thread child = new Thread(){

public void run(){

//DeadLockQueue.this.enqueue(workItem);

System.out.println("在将对象入队列 "+workItem);

enqueue(workItem);

}

};

child.start();

child.join();//dead lock

}

}

4.保持可运行线程数量尽可能的少的主要技术是,让每个线程做少量的工作,然后使用Object.wait等待某个条件发生,或者使用Thread.sleep()睡眠一段时间,线程不应该忙-等busy-wait,即反复的检查一个数据结构,以等待某些事件发生。除了使程序易受调度器的变化的影响外,忙等这种做法还会增加处理器的负担

busy-wait

Java代码



package effective.java;

import java.util.LinkedList;

import java.util.List;

public abstract class WorkQueueBusyWait {

private final List queue = new LinkedList();

private boolean stopped = false;

protected WorkQueueBusyWait(){

new WorkThread().start();

}

public final void enqueue(Object workItem){

synchronized(queue){

queue.add(workItem);

}

}

public final void stop(){

synchronized(queue){

stopped = true;

}

}

protected abstract void processItem(Object workitem) throws InterruptedException;

private class WorkThread extends Thread{

public void run(){

final Object QUEUE_IS_EMPTY = new Object();

while(true){

Object workItem = QUEUE_IS_EMPTY;

synchronized(queue){

if(stopped)

return;

if(!queue.isEmpty())

workItem = queue.remove(0);

}

if(workItem != QUEUE_IS_EMPTY){

try{

processItem(workItem);

}catch(InterruptedException ie){

ie.printStackTrace();

return;

}

}

}

}

}

}

class PingPongQueue extends WorkQueue{

volatile int count=0;

@Override

protected void processItem(final Object workItem) throws InterruptedException {

count++;

WorkQueue recipient = (WorkQueue)workItem;

recipient.enqueue(this);

}

}

package effective.java;

public class WaitQueuePerf {

/** *//**

* @param args

*/

public static void main(String[] args) {

PingPongQueue q1 = new PingPongQueue();

PingPongQueue q2 = new PingPongQueue();

q1.enqueue(q2);

try{

Thread.sleep(1000);

}catch(InterruptedException ie){

ie.printStackTrace();

}

int count = q1.count;

try{

Thread.sleep(1000);

}catch(InterruptedException ie){

ie.printStackTrace();

}

System.out.println(q1.count-count);

q1.stop();

q2.stop();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: