线程状态:
线程可以处于下面的四种状态之一:
新建(NEW)
线程被创建成功之后会短暂处于这个状态。线程已分配了必需的系统资源。并执行了初始化。此时线程已经有资格获得cpu时间片了。此后调度器将把这个线程转变为可运行或阻塞状态。
就绪(Rannable)在这种情况下,线程只要分配到时间片,他就运行。
阻塞(Blocked)线程能够运行,但是某种条件阻止了他的运行。当线程阻塞时候。调度器会忽略阻塞线程。不会分配时间片给它,直到线程重新进入可运行状态,才能继续执行操作。
死亡(Dead)处于死亡或者终止状态的线程,将不会被调度。他的任务已结束。或者不在是可运行的。任务线程死亡通常是从run 方法中返回。但是线程还是可以被中断的。
进入阻塞状态:
- sleep()
- await()
- 等待输出/输入完成
- 同步 等待锁
在较早的代码中可能使用 suspend() 和 resume() 来唤醒线程 (留坑、这个两个方法如何唤醒。为什么会导致死锁) ,但是现在已经被废止了(会导致死锁),stop() 也被废止了。因为它不释放线程获得的锁。
中断
下面将演示使用Executor展示interrupt()的用法:
public class SleepBlock implements Runnable { @Override public void run() { // TODO Auto-generated method stub try { TimeUnit.MILLISECONDS.sleep(600); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("SleepBlock Interrupted"); } System.out.println("Exit SleepBlock run"); }}
public class SynchronizedBlock implements Runnable { public SynchronizedBlock() { new Thread(this) { @Override public void run() { // TODO Auto-generated method stub f(); } }.start(); } public synchronized void f() { while (true) { Thread.yield(); } } @Override public void run() { // TODO Auto-generated method stub System.out.println("try invoke fn"); f(); System.out.println("Exit synchronized run "); }}
public class IOBlock implements Runnable { private InputStream in; public IOBlock(InputStream in) { super(); this.in = in; } @Override public void run() { // TODO Auto-generated method stub // 暂无演示 } }
public class InterruptTest { private static ExecutorService exec = Executors.newCachedThreadPool(); static void test(Runnable target) { Future f = exec.submit(target); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("interrupting " + target.getClass().getName()); f.cancel(true); System.out.println("interrupt sent to " + target.getClass().getName()); } public static void main(String[] args) throws Exception { test(new SleepBlock()); test(new SynchronizedBlock()); TimeUnit.SECONDS.sleep(3); System.out.println("system exit(0)"); System.exit(0); }}
运行结果:
上面总共表示了3种阻塞SleepBlock表示可中断阻塞,而IOBlock和SynchronizedBlaock表示不可中断阻塞。他们都是不可以被中断的。通过观察也可以知道 这两个不需要 InterruptException处理。 ** 从输出可以看出,能够中断对sleep()的调用。(或者任何抛出InterruptException的操作) 但是你不能中断 IOBlock 和SynchronizedBlock 的操作。 这点有些令人烦恼特别是 执行IO程序的时候,这以为着他将锁住你的多线程程序的性能。特别是web程序。
public class CloseResourceTest { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); ServerSocket server = new ServerSocket(9292, 1, InetAddress.getByName("127.0.0.1")); Socket socket = new Socket("127.0.0.1", 9292); InputStream in = socket.getInputStream(); exec.execute(new IOBlock(in)); exec.execute(new IOBlock(System.in)); TimeUnit.MILLISECONDS.sleep(100); System.out.println("showdown all Thread"); exec.shutdownNow(); TimeUnit.SECONDS.sleep(1); System.out.println("closing " + in.getClass().getName()); in.close(); TimeUnit.SECONDS.sleep(1); System.out.println("closing:"+System.in.getClass().getName()); System.in.close(); }}
public class IOBlock implements Runnable { private InputStream in; public IOBlock(InputStream in) { super(); this.in = in; } @Override public void run() { // 暂无演示 try { System.out.println("try to read"); in.read(); } catch (IOException e) { if (Thread.currentThread().interrupted()) { System.out.println("interupt IOBlock"); } else { throw new RuntimeException(); } } System.out.println("Exit IOBlock run"); }}
输出:
这个程序证明了 关闭底层资源之后 任务将解除阻塞。 nio 提供了更人性化的操作。被阻塞的nio通道会自动的响应中断。 (演示暂无 留坑) 一个锁多次由同一个任务获得:
public class MutiLock { public synchronized void f1(int count) { if (count-- > 0) { System.out.println("f1:" + count); f2(count); } } public synchronized void f2(int count) { if (count-- > 0) { System.out.println("f2:" + count); f1(count); } } public static void main(String[] args) { new Thread() { @Override public void run() { new MutiLock().f1(10); } }.run(); }}
JAVA SE5 并发库中新添加一个特性,即在ReentrantLock上阻塞的任务具有中断的能力:
这与synchronized的阻塞完全不同。
public class BlockedMutex { private Lock lock = new ReentrantLock(); public BlockedMutex() { lock.lock(); } public void f(){ try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println("lock intrupt"); } }}
public class Blocked2 implements Runnable { BlockedMutex block = new BlockedMutex(); @Override public void run() { System.out.println("try lockinterruptly"); block.f(); System.out.println("exiting Blocked2 run"); } public static void main(String[] args) throws Exception { Thread t = new Thread(new Blocked2()); t.start(); TimeUnit.SECONDS.sleep(1); System.out.println("to interrupt"); t.interrupt(); }}
从运行结果可以看出这个被打断了。
中断检查
public class NeedClean { private final int id; public NeedClean(int id) { this.id = id; System.out.println(this); } @Override public String toString() { return "NeedClean [id=" + id + "]"; } public void clean() { System.out.println("clean :" + this); }}
public class Blocked3 implements Runnable { public static void main(String[] args) throws Exception { Thread t = new Thread(new Blocked3()); t.start(); TimeUnit.MILLISECONDS.sleep(110); System.out.println("send interrupt sign"); t.interrupt(); } @Override public void run() { try { while (!Thread.interrupted()) { // point1 NeedClean need1 = new NeedClean(1); try { System.out.println("sleep"); TimeUnit.MILLISECONDS.sleep(100); // blocking operation // point 2 NeedClean need2 = new NeedClean(2); try { System.out.println("counting"); for (long i = 0; i < 2500000000l; i++) { @SuppressWarnings("unused") double d = (Math.E + Math.PI) / i + Math.E + Math.PI % 0.3 + 16591; } System.out.println("finish counting"); } finally { need2.clean(); } } finally { need1.clean(); } } } catch (InterruptedException e) { System.out.println("exit ....."); } // TODO Auto-generated method stub }}
中断发生在 Sleep:
中断发生在 counting:
解释一下输入
- 如果中断发生在 point 2 之后就是阻塞操作sleep 之后 非阻塞的运算之中时候 会执行完 for循环 然后执行finally 从 顶部的额while跳出循环
- 如果中断发生在 point1 和point2 之间(在sleep之前或者sleep之中)的话,那么任务在执行第一次阻塞操作之前 经由InterruptedException退出。 介绍此类的 只要目的是为了说明 在涉及相应 interrupt 的类和程序时候 一定要做好 清理策略
关于形成 interrupt interrupted isinterrupted 的区别:
1、interrupt interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。 注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。
2、interrupted 和 isInterrupted
首先看一下该方法的实现:
public static boolean interrupted () { return currentThread().isInterrupted(true); } 该方法就是直接调用当前线程的isInterrupted(true)方法。 然后再来看一下 isInterrupted的实现: public boolean isInterrupted () { return isInterrupted( false); } 这两个方法有两个主要区别: interrupted 是作用于当前线程,isInterrupted 是作用于调用该方法的线程对象所对应的线程。(线程对象对应的线程不一定是当前运行的线程。例如我们可以在A线程中去调用B线程对象的isInterrupted方法。) 这两个方法最终都会调用同一个方法,只不过参数一个是true,一个是false;第二个区别主要体现在调用的方法的参数上,让我们来看一看这个参数是什么含义
先来看一看被调用的方法 isInterrupted(boolean arg)的定义:
private native boolean isInterrupted( boolean ClearInterrupted);
原来这是一个本地方法,看不到源码。不过没关系,通过参数名我们就能知道,这个参数代表是否要清除状态位。
如果这个参数为true,说明返回线程的状态位后,要清掉原来的状态位(恢复成原来情况)。这个参数为false,就是直接返回线程的状态位。这两个方法很好区分,只有当前线程才能清除自己的中断位(对应interrupted()方法)
总结的来说 就是 isinterrupted 每次只会查询标志位 不会改变标志位的状态
而 interrupted 测试线程是否中断 线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
public class Interrupt { public static void main(String[] args) throws Exception { Thread t = new Thread(new Worker()); t.start(); Thread.sleep(20); t.interrupt(); System.out.println("Main thread stopped."); } public static class Worker implements Runnable { public void run() { System.out.println("Worker started."); try { for (long i = 0; i < 2500000000l; i++) { @SuppressWarnings("unused") double d = (Math.E + Math.PI) / i + Math.E + Math.PI % 0.3 + 16591; } } finally { System.out.println(Thread.interrupted()); // 如果为true的话 会重置标志位 为false 如果为false的话那就不会重置 System.out.println(Thread.interrupted()); System.out.println(Thread.interrupted()); } System.out.println("Worker stopped."); } }}
强调一点 抛出InterruptedException 时候jvm 会丢失当前线程的的中断标志,所以在catch 块中的 interrupted或者isInterrupted 都返回false