一个线程可以处于以下四种状态:
1:新建(new):当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必需的系统资源,并执行了初始化。此时刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。
2:就绪(Runnable):这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行,这不同于死亡和阻塞状态。
3:阻塞(Blocked):线程能够运行,但又某个条件阻止它运行。当线程处于阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间,直到线程重新进入了就绪状态,它天才有可能执行操作。
4:死亡(Dead):处于死亡或终止状态的线程将不在时可调度的,并且在也不会得到CPU时间,它的任务已结束,或不再时可运行的,任务死亡的通常方式时从run()方法种返回,但是任务的线程还可以被中断,你将看到这一点。
进入阻塞状态,可能有以下原因:
1:sleep休眠。
2:通过调用wait()将线程挂起。直到线程得到了notify()或notifyAll()。
3:任务在等待某个输入/输出完成。
4:任务试图在某个对象上表用其他同步控制方法,但是对象锁不可用,因为另外一个任务已经获取了锁。
当你打断被阻塞的任务时,可能需要清理资源。正因为这点,在任务的run()方法中间打断,更像是抛出的异常,因此在java线程中的这种类型的异常中断中用到了异常。所以需要仔细编写try-catch子句以正确清理所有的事物。
Thread类包含Interrupt()方法,因此你可以中断被阻塞的任务,这个方法将设置线程的中断状态。如果一个线程已经阻塞或者试图执行阻塞操作,此时调用这个方法将抛出InterruptedException异常。
当抛出该异常或者该任务调用Thread.interrupt方法时,中断状态将被复位。
Thread.interrupt()方法提供了离开run()而不抛出异常的第二种方式。为了调用Thread.interrupt()需要持有一个Thread对象。
新的current类库提供的Executor似乎在避免对Thread对象的直接操作。
若在Executor上调用shutdownNow(),那么它将发送一个interrupt()调用给它启动的所有线程。
cancel(boolean)是一种中断由Executor启动的单个线程的方式。而此时,任务应该由submit()方法启动而不是execute()方法。
submit()方法将返回一个范型Future<?>,在其上调用cancel(boolean)方法(参数为true)可以终止此任务。
class SleepBlocked implements Runnable{
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(100);
}catch(InterruptedException e) {
System.out.println("InterruptedException");
}
System.out.println("Exiting SleepBlocked.run()");
}
}
class IOBlocked implements Runnable{
private InputStream in;
public IOBlocked(InputStream is) {in=is;}
@Override
public void run() {
try {
System.out.println("Waiting for read():");
in.read();
}catch(IOException e) {
if(Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted from blocked I/O");
}
else {
throw new RuntimeException(e);
}
}
System.out.println("Exiting IOBlocked.run()");
}
}
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 SynchronizedBlocked.run()");
}
}
public class Interrupting {
private static ExecutorService exec=Executors.newCachedThreadPool();
static void test(Runnable r) throws InterruptedException{
Future<?> f=exec.submit(r);
TimeUnit.MILLISECONDS.sleep(100);
System.out.println("Interrupting "+r.getClass().getName());
f.cancel(true);
System.out.println("Interrupting sent to "+r.getClass().getName());
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
test(new SleepBlocked());
test(new IOBlocked(System.in));
test(new SynchronizedBlocked());
TimeUnit.MILLISECONDS.sleep(3);
System.out.println("Absorbing with System.exit(0)");
System.exit(0);
}
}
/*
Exiting SleepBlocked.run()
Interrupting demo.SleepBlocked
Interrupting sent to demo.SleepBlocked
Waiting for read():
Interrupting demo.IOBlocked
Interrupting sent to demo.IOBlocked
Trying to call f()
Interrupting demo.synchronizedBlocked
Interrupting sent to demo.synchronizedBlocked
Absorbing with System.exit(0)
*/
以上程序的每个任务都表示了一种不同类型的阻塞。SleepBlock()是可中断的阻塞示例,而IOBlocked和SynchronizedBlocked是不可阻断的阻塞示例。所以I/O和在synchronized块上的等待是不可中断的,浏览代码可以发现无论是I/O还是尝试调用synchronized方法,都不需要任何InterruptedException处理器。
在SynchronizedBlocked中,我们必须首先获取锁,这是通过创建一个匿名的Thread类的一个实例来实现的,它调用了f()先获取了对象锁。因为f()方法内是一个死循环,永远不会返回,所以永远不会释放锁。此时Synchronized.run()方法在试图获取对象锁,并阻塞等待这个锁释放。
从以上程序示例看出:你不能中断正在试图获取synchronized锁或者试图执行I/O操作的线程。
至于更加人性化中断I/O的nio类,等咱学完了再补上。
class BlockedMutex{
private Lock lock=new ReentrantLock();
public BlockedMutex() {
lock.lock();
}
public void f() {
try {
lock.lockInterruptibly();
System.out.println("lock acquired in f()");
}catch(InterruptedException e) {
System.out.println("Interruppted from lock acquisition in f()");
}
}
}
class Blocked2 implements Runnable{
BlockedMutex blocked=new BlockedMutex();
public void run() {
System.out.println("Waiting for f() in BlockedMutex");
blocked.f();
System.out.println("Broken out of blocked call");
}
}
public class Interrupting2 {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread t=new Thread(new Blocked2());
t.start();
TimeUnit.MILLISECONDS.sleep(1);
System.out.println("Issuing t.interrupt()");
t.interrupt();
}
}
以上程序:java SE5添加了一个特性,即在ReentrantLock上阻塞的任务具备可以被中断的能力,这与在synchronized方法或临界区阻塞的任务不同。