标签:
下面列举线程进入阻塞状态的几个可能原因:
由于处于阻塞状态的线程被挂起,得不到执行,即是代码中有判断状态值某一点而退出,亦不会到达该点,这种情况下应该强制任务跳出阻塞状态。
Thread类包含interrupt方法,用于终止被阻塞的任务。调用interrupt方法将设置线程的中断状态,如果一个线程已经被阻塞或者试图执行一个阻塞操作,那么设置线程的中断状态将抛出InterruptedException异常。当抛出InterruptedException异常或者该任务调用Thread.interrupted()时,线程中断状态将被重置。这个中断被阻塞的线程需要我们持有线程对象。
Java SE5起建议使用Executor来创建并发任务,当在Executor上调用shutdownNow(),那么它将发生一个interrupt给它启动的所有线程。如果只是想关闭某个特定而不是全部的任务,要通过submit来提交任务,返回Future<?>对象,在Future对象上调用cancel来中断线程,也可以传递true给cancel,那么Future对象就会拥有在该线程上调用interrupt以终止线程的权限。
下面看看不同阻塞条件下线程中断情况:
//定义sleep进入阻塞的任务
public class SleepBlocked implements Runnable{ public void run() { try { TimeUnit.SECONDS.sleep(10); }catch (InterruptedException e) { System.out.println("interrupt from sleep..."); } System.out.println("exiting from SleepBlocked run"); } } //定义因为IO进入阻塞的任务 public class IOBlocked implements Runnable { InputStream input; public IOBlocked(InputStream input) { this.input = input; } public void run() { try { System.out.println("waiting for input..."); input.read(); }catch (IOException e) { System.out.println("IOException..."); if(Thread.interrupted()) { System.out.println("interrupt from IOBlocked..."); }else { throw new RuntimeException(); } } System.out.println("exiting from IOBlocked......"); } } //定义因为尝试获取对象锁进入阻塞的任务 public class SynchronizedBlocked implements Runnable{ public synchronized void f() { while (true) Thread.yield(); } public SynchronizedBlocked() { new Thread() { public void run() { f(); } }.start(); } public void run() { System.out.println("trying to call f()..."); f(); System.out.println("exiting from SynchronizedBlocked...."); } }
测试类如下:
public class InterruptedTest { private static ExecutorService executor = Executors.newCachedThreadPool(); static void test(Runnable task) throws InterruptedException{ //使用Executor的submit方法提交任务,或者线程的参考,以便中断线程
Future future = executor.submit(task); TimeUnit.SECONDS.sleep(1); System.out.println("interruptting "+task.getClass().getName());
//通过传递true给Future对象的cancel方法,运行中断被阻塞的线程 future.cancel(true); System.out.println("interrupt send to "+task.getClass().getName()); } public static void main(String[] args) throws Exception{ test(new SleepBlocked()); test(new IOBlocked(System.in)); test(new SynchronizedBlocked()); } }
输出:
interruptting com.thread.test.SleepBlocked
interrupt send to com.thread.test.SleepBlocked
interrupt from sleep... //因为睡眠而阻塞的线程被中断
exiting from SleepBlocked run
//因为IO而阻塞的线程没有被中断
waiting for input...
interruptting com.thread.test.IOBlocked
interrupt send to com.thread.test.IOBlocked
//因为获取对象锁而阻塞的线程没有被中断
trying to call f()...
interruptting com.thread.test.SynchronizedBlocked
interrupt send to com.thread.test.SynchronizedBlocked
exiting from IOBlocked......
从输出可以看出,可以中断对sleep的调用,但是不能中断试图获取对象锁(synchronized实现)或者执行IO任务的线程。
从上面可以知道,以synchronized关键字实现的同步对象锁在阻塞时不能被中断。这里介绍一种既可以提供锁功能,又能够在阻塞时被中断的实现,那就是使用ReentrantLock。
public class BlockedMutex{ private Lock lock = new ReentrantLock(); public void f() { try {
//一直占有锁,除非被中断 lock.lockInterruptibly(); }catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+" interrupted from lock acquisition in f()"); } } } public class LockBlocked implements Runnable { private BlockedMutex blockedMutex; public LockBlocked(BlockedMutex blockedMutex) { this.blockedMutex = blockedMutex; } public void run() { System.out.println(Thread.currentThread().getName()+" waiting for blockedMutex...."); blockedMutex.f(); System.out.println(Thread.currentThread().getName()+" Broken out of blocked call"); } } public class InterruptedTest {public static void main(String[] args) throws Exception{ BlockedMutex blockedMutex = new BlockedMutex(); Thread t1 = new Thread(new LockBlocked(blockedMutex)); Thread t2 = new Thread(new LockBlocked(blockedMutex)); t1.start(); t2.start(); t2.interrupt(); } }
输出:
Thread-0 waiting for blockedMutex....
Thread-1 waiting for blockedMutex....
Thread-0 Broken out of blocked call
Thread-1 interrupted from lock acquisition in f()
Thread-1 Broken out of blocked call
两个任务都是使用同一个BlockedMutex实例的f方法,f方法里面是以永久占有锁的方式获取对象锁,这样只有最先被执行的任务能够占有锁,之后的任务将一直等待。测试中见到t1占有锁,t2在等待锁,中断t2,看到t2驱动的任务里面抛出InterruptedException,这也说明ReentrantLock锁确实能够在阻塞下被中断。
从上面的学习得知,在线程进入阻塞或者要进入阻塞时,调用线程的interrupt,线程将会抛出异常(IO阻塞或者synchronized阻塞例外,不能被中断)。在任务里面,并不都是可能导致线程进入阻塞的代码,在任务执行不会导致阻塞的代码时,可以通过调用Thread的interrupted方法来检查中断状态,因为Thread的interrupt方法会设置线程的中断标志位,而interrupted方法则能够读取到该标志位,并且重置标志位。请看下面例子:
public class InterruptedCheck implements Runnable { private double d = 1d; public void run() { try { while (!Thread.interrupted()) { System.out.println("sleeping...");
//线程睡眠2s,进入阻塞,如果在睡眠时间interrupt,将会抛出异常 Thread.sleep(2000); System.out.println("calculating....");
//这里以运算模拟任务,要有一定的时间,但是线程不会进入阻塞状态。如果在运算期间进行interrupt,将不会抛出异常,且中断标志位被设置 for(int i=1;i<25000000;i++) { d = d + (Math.PI + Math.E) /d; } } System.out.println("detected interrupted, not from blocked..."); }catch (InterruptedException e) { System.out.println("interrupted from blocked..."); } } } public static void main(String[] args) throws Exception{ Thread thread = new Thread(new InterruptedCheck()); thread.start();
//这里man的睡眠时间为不同的值,就可以在thread处于不同的状态(sleep进入阻塞或者正常执行运算)interrupt TimeUnit.MILLISECONDS.sleep(2100); System.out.println("interruptting..."); thread.interrupt(); } 输出:main的sleep时间为1500milsec时 sleeping... interruptting... interrupted from blocked...
main的sleep时间为2100milsec时
sleeping...
calculating....
interruptting...
detected interrupted, not from blocked...
可以看到,在可以被中断的阻塞状态下中断线程,将会以抛出异常的形式退出任务。在运行状态下中断线程,被中断的线程的中断标志位被设置,通过interrupted方法可以读取到线程该标志位,从而判断线程是否被中断,进而执行退出任务的策略判断。
标签:
原文地址:http://www.cnblogs.com/lauyu/p/5097565.html