Thread类中的中断分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Andyzhu_2005/article/details/82886316

interrupt()分析

先看javadoc里的关于Thread类的interrupt()方法说明

Interrupts this thread. 

Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown. 

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException. 

If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a java.nio.channels.ClosedByInterruptException. 

If this thread is blocked in a java.nio.channels.Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked. 

If none of the previous conditions hold then this thread's interrupt status will be set. 

Interrupting a thread that is not alive need not have any effect.

Throws:
SecurityException - if the current thread cannot modify this thread
@revised
6.0
@spec
JSR-51

线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。判断某个线程是否已被发送过中断请求,请使用Thread.currentThread().isInterrupted()方法(因为它将线程中断标示位设置为true后,不会立刻清除中断标示位,即不会将中断标设置为false),而不要使用thread.interrupted()(该方法调用后会将中断标示位清除,即重新设置为false)方法来判断,
除非线程本身自己允许中断自己,其他线程中断此线程,有可能会引发异常。这里要分为两种情况:
一种是此线程正在处于非阻塞状态,,其他线程调用此类的interrupt方法时,会将线程的中断标志位设为true,(通过isInterrupted查询),

public class InterruptTest2 extends Thread {
	
	private boolean flag=true;

	public InterruptTest2(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (flag) {

	               if (currentThread().isInterrupted()) {
	            	   System.out.println("已中断");
					
				}else{
					i++;
	                System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
				}
	                
	            }
	        } 
	    }
		

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest2 test=new InterruptTest2("t1");
		test.start();
		 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 test.interrupt();
      

         // 主线程休眠300ms,然后查看t1的状态。
         try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
     
		

	}

}

在main方法中调用 test.interrupt();后,test的子线程(run方法)会继续运行,不停输出已中断
已中断
已中断
已中断
已中断
说明interrupt()方法只会设置线程的中断位,而线程会继续执行。(即我们需要通过获得这个标志位来进行中断后的逻辑分析)。

二、如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait、1.5中的condition.await及可中断的通道上的 I/O 操作方法)调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。

public class InterruptTest2 extends Thread {
	
	private boolean flag=true;

	public InterruptTest2(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (flag) {
	            	
	            	try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						System.out.println("线程状态-->"+isInterrupted());
						flag=false;   //设置flag=false  跳出循环
						e.printStackTrace();
					}
	                
	            }
	        } 
	    }
		

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest2 test=new InterruptTest2("t1");
		test.start();
		 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		 test.interrupt();
      

         // 主线程休眠300ms,然后查看t1的状态。
         try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
     
		

	}

}

执行结果:

java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.andy.thread.notify.InterruptTest2.run(InterruptTest2.java:33)
线程状态false
t1 (RUNNABLE) loop 1

说明:正在阻塞的线程被外部中断,会导致报InterruptedException 异常,然后中断标志位被设为false。我们可以在异常中进行相关的逻辑处理,跳出子线程。(代码中是将flag设为false,从而跳出while循环,最终子线程执行完毕)

Thread.interrupted()和Thread.currentThread().isInterrupted()(或者直接用isInterrupted())区别

Thread.interrupted(),看其javadoc说明:

Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it). 

A thread interruption ignored because a thread was not alive at the time of the interrupt will be reflected by this method returning false.


Thread.interrupted()用于判断线程是否被中断,并且其中断标志位会在这个方法中清除。也就是说如果这个方法被成功执行2次,则第一次返回true,第二次返回false。因为在第一次调用过程中,其标志位被设置为了false。

而Thread.currentThread().isInterrupted()只会返回线程的中断标志位,多次调用,返回值是一致的。

一个线程调用另一个线程的非run方法的Thread指向

这个问题是在看FutureTask的源码时的一点疑惑。即在main线程中调用子线程的非run方法,Thread.interrupted()到底是指谁?

public class InterruptTest5 extends Thread {
	
	private boolean flag=true;

	public InterruptTest5(String name){
		super(name);
	}
	
	@Override
	public void run(){
		  {  
	            int i=0;
	            while (!isInterrupted()) {
	            	
	            	try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

	               if (isInterrupted()) {
	            	   System.out.println("已中断");
					
				}else{
					i++;
	                System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
				}
	                
	            }
	        } 
	    }
	
	
	
	public void stopThread(){
		
		Thread.currentThread().interrupt();
		System.out.println("stop方法中:"+Thread.currentThread().getName());

	}
	
		public void checkThread(){
			
			System.out.println("checkthread方法中:"+Thread.currentThread().getName());
			System.out.println("checkThread方法中"+Thread.currentThread().getName()+"-->"+Thread.interrupted());
			if (Thread.interrupted()) {
				System.out.println(Thread.currentThread().getName()+"线程被中断");
			}
		}



}

上面是一个线程类,除了定义run方法,还设置了stopThread()和checkThread()方法。
在main方法中调用这个类。

public static void main(String[] args) {
		// TODO Auto-generated method stub
		InterruptTest5 test5=new InterruptTest5("线程5");
		
		test5.start();
		
  
	//	Thread.currentThread().interrupt();
		
		test5.stopThread();
		
		//System.out.println(Thread.currentThread().getName()+"-->"+Thread.interrupted());
		
		test5.checkThread();

	}

运行结果:

stop方法中:main
checkthread方法中:main
checkThread方法中main-->true
线程5 (RUNNABLE) loop 1
线程5 (RUNNABLE) loop 2
线程5 (RUNNABLE) loop 3

分析:main方法调用了子线程的stopThread()方法,通过打印
System.out.println(“stop方法中:”+Thread.currentThread().getName());
其线程名是main,说明stopThread()所处的线程仍然是main线程。
再调用checkThread()进行查询线程状态,通过
System.out.println(“checkThread方法中”+Thread.currentThread().getName()+"–>"+Thread.interrupted());,
其结果是
checkThread方法中main–>true
说明,主线程main的中断标志位被设为false。而子线程仍然在sleep,没有被中断。
而checkThread()中最后的:
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName()+“线程被中断”);
}
为什么没有被执行呢?因为上面一句
System.out.println(“checkThread方法中”+Thread.currentThread().getName()+"–>"+Thread.interrupted());,
后,其中断标志位被设为了false,所以后面的if没有被执行。这也是上一节说明的

猜你喜欢

转载自blog.csdn.net/Andyzhu_2005/article/details/82886316