线程的中断 线程在运行完run后会自动死亡 但是有的时候我们需要在run方法中的某一处中断该线程
那么我们就可以通过一个标识符 的true 和false来判定是否终结任务 并且可以通过为标识符赋值来加以控制
下面演示一个实例
演示一个花园想要通过每天通过大门的人数来了解进入公园的访问量,每一个大门都有一个计数器,并且任何一个门的计数器递增时 公园中的总人数共享计数值也会递增,
公园共享的总人数记数类
package test.thread.zhongjiahuayuan; import java.util.Random; /** * 计数类 * @author Administrator * */ public class Count { private int count = 0; private Random rang = new Random(47); public synchronized int increment(){ int temp = count; if(rang.nextBoolean()){ /** * 线程让步 让步一半的时间 */ Thread.yield(); } return (count = ++temp); } public synchronized int vlaue(){return count;} }
定义花园的门的类
package test.thread.zhongjiahuayuan; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class Entrance implements Runnable { /** * 计数 游客访问量 */ private static Count count = new Count(); /** * 花园所有的进口集合 */ private static List<Entrance> entrances = new ArrayList<Entrance>(); /** * 一个进口的访问量 */ private int number = 0; /** * 花园进口的标识id */ private final int id; /** * 任务取消标识 */ private static volatile boolean canceled = false; /** * 取消任务方法 */ public static void cancel() { canceled = true; } public Entrance(int id) { this.id = id; entrances.add(this); } @Override public void run() { /** * 任务没取消也就是没有关门的情况下 模拟游客进入。 */ while (!canceled) { synchronized (this) { number++; /** * 递增递减=不是原子操作所以 必须同步 */ } /** * 游客访问总量增加 */ System.out.println(this + "Total:" + count.increment()); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public synchronized int getValue(){ return number; } public String toString(){ return "Entrance " + id+": "+ getValue(); } public static int getTotalCount(){ return count.vlaue(); } public static int sumEntrances(){ int sum =0; for(Entrance entrance:entrances){ sum+= entrance.getValue(); } return sum; } }
定义测试类 定义五个门 每个门开放3秒钟 3秒结束 线程运行结束
package test.thread.zhongjiahuayuan; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class OrnamentalGarden { public static void main(String[] args) throws InterruptedException { ExecutorService exc = Executors.newCachedThreadPool(); for(int i=0;i<5;i++){ exc.execute(new Entrance(i)); } /** * 三秒关门 */ TimeUnit.SECONDS.sleep(3); Entrance.cancel(); exc.shutdown(); /** * 判断该线程池在规定时间内 是否完成所有任务 */ if(!exc.awaitTermination(250, TimeUnit.MILLISECONDS)) System.out.println("Some tasks were not terminted"); System.out.println("Total:"+Entrance.getTotalCount()); System.out.println("sum of Entrance :"+Entrance.sumEntrances()); } }
上面演示了的是 在线程非阻塞的情况下中断线程 基本上都是在就绪的状态时 进行的中断 然而有时我们想要在线程阻塞的情况下进行中断 ,在详细演示这种中断之前 先来了解一下线程的几种状态
线程的状态
1 新建状态 当线程被创建的时。它只会短暂的处于这种状态,此时他已经分配了必须的系统资源
并执行了初始化 此刻线程已经有资格获取cpu的时间了 之后调度器将把这个线程转变为可运行的状态和阻塞状态
2 就绪状态 在这种状态下 只要调度器把时间片分配给线程,线程就可以运行,也就是说 ,在任意时刻 线程可以运行也可以不运行,只要调度器能分配时间片给线程 他就可以运行 这与死亡和阻塞状态不同
3 阻塞状态: 线程能够运行 但是有某个条件阻止它的运行,当线程处于阻塞状态的时候,调度器将忽略该线程,不会分配给该线程运行所需要的cpu时间片,直到线程重新进入就绪状态,它才能执行操作。
4 死亡 处于死亡或者终止状态的线程是不可以再一次调度的,并且也不会得到cpu的时间片,他的任务已经结束,或不再是可以运行的了,任务死亡通常是从run方法返回 但是任务的线程还可以中断。
一个任务进入阻塞的状态
一个任务进入阻塞的状态
1 通过调用sleep方法进入休眠的状态。
2 通过 调用wait使线程挂起 直到线程得到了notify 或者notifyAll消息 线程才会进入就绪状态
3 任务在等待某个输入或者输出
4 任务试图在某个对象上调用其同步的控制方法 但是对象锁不可用 因为另一任务已经获取了这个锁,
现在我们需要关心的是 能够终止处于阻塞状态的任务 如果对于处于阻塞状态的任务用 你不能等待其大袋代码中可以检查其状态值的么偶一点因而决定让他主动终止,那么你必须强制这个任务任务跳出阻塞状态
中断
所以为了强制让他跳出阻塞的状态,我们可以在run方法中中断它 但是这种中断线程的这种方式 相当于在run方法中抛出了一个中断异常 而异常后的正常的清理资源的代码不会去执行 所以 中断以后 我们必须仔细的编写catch语句正确的清楚所有的事物。
Thread类提供了interrupt方法,所以你可以终止被阻塞的任务 这个方法将设置线程中断的状态 ,如果一个线程已经被阻塞或者正在试图去执行一个阻塞的操作,那么设置这个线程的中断将抛出InterruptedException异常 当抛出这个异常或者执行Thread.interrupt的时候,中断状态会被复位,但是现在已经不提倡这种做法了 我们可以和使用Excecutor 来执行操作,
看下面的例子
几种阻塞的线程实例
package test.thread.zhongjiahuayuan.interrupt; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; public class SleepBlocked implements Runnable { /** * 可中断的的阻塞实例 */ @Override public void run() { try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("InterruptedException"); } System.out.println("Exiting SleepBlocked.run()方法"); } } /** * 不可以中断的阻塞状态 在等待io流的时候 是不可以直接中断的 * @author Administrator * */ class IOBlocked implements Runnable { private InputStream in; public IOBlocked(InputStream is) { in = is; } @Override public void run() { System.out.println("Wating for Read()"); try { in.read(); } catch (IOException e) { if (Thread.currentThread().isInterrupted()) { System.out.println("Interrupted from IOBlocked I/O "); }else { throw new RuntimeException(e); } } System.out.println("Exiting IOBlocked 的run方法"); } } /** * 为了模拟在等待获取对象的同步锁. * @author Administrator * */ class SynchronizedBlocked implements Runnable{ public synchronized void f(){ while(true){ Thread.yield(); } } public SynchronizedBlocked() { /** * 先用匿名内部类 占上该对象的同步锁 然后一直 让步但是并不释放同步锁所以 同步锁一直不会返回 * 这样也是不可以中断的 */ new Thread(){ public void run(){ f(); } }.start(); } @Override public void run() { System.out.println("Trying to call f()"); f(); System.out.println("正在推出SynchronizedBlocked的run方法"); } }
然后写中断测试
package test.thread.zhongjiahuayuan.interrupt; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class Interrupting { private static ExecutorService exec = Executors.newCachedThreadPool(); /** * 统一设置中断方法 * @param r * @throws InterruptedException */ public static void test(Runnable r) throws InterruptedException{ Future<?> f = exec.submit(r); TimeUnit.MICROSECONDS.sleep(100); System.out.println("Inteerupt "+r.getClass().getName()); f.cancel(true);//中断标识设置为中断 System.out.println("Interrupt sent to "+r.getClass().getName()); } public static void main(String[] args) throws Exception { test(new SleepBlocked()); test(new IOBlocked(System.in)); test(new SynchronizedBlocked()); TimeUnit.SECONDS.sleep(3); System.out.println("Aborting with System(0)"); System.exit(0); } }
结果证明了 第一种sleep阻塞是可以被中端的 而第二种的等待io输出输入是不可以被中断的 第三种等待获取同步锁也是不可以中断的 所以会引发死锁 这个以后的文章再说。