最近在做项目,遇到一个问题:如果停止一个Java线程。

Java推荐的标准方法:使用interrupt终止线程
如何使用interrupt中断一个线程?通常的做法是在线程外部调用interrupt方法,线程内部会接收到相应异常,然后在异常处理中安全退出线程。请看下面的例子:

public class MyThread implements Runnable
{
    private Thread runner = new Thread(this, "runner");

    public void start()
    {
        runner.start();
    }

    public void interrupt()
    {
        runner.interrupt();
    }


    @Override
    public void run()
    {
        while(true)
        {
            try
            {
                Thread.sleep(1000);
                System.out.println("thread is running.");
            }
            catch(Exception e)
            {
                System.out.println("thread exit.");         
                break;

            }
        }
    }

    public static void main(String[] args) 
    {
        MyThread myThread = new MyThread();     

        try
        {
            myThread.start();
            Thread.sleep(5000);
            myThread.interrupt();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
}

那么,是否调用interrupt方法,线程内部都会收到响应异常?其实不是这样,如果线程内部没有调用sleep/wait/join等方法,线程是不会接收到响应异常的。请看看下面的例子。

public class MyThread implements Runnable
{
    private Thread runner = new Thread(this, "runner");

    public void start()
    {
        runner.start();
    }

    public void interrupt()
    {
        runner.interrupt();
    }


    @Override
    public void run()
    {
        try
        {
            while(true)
            {
                int a = 1;
            }
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }

    public static void main(String[] args) 
    {
        MyThread myThread = new MyThread();     

        try
        {
            myThread.start();
            Thread.sleep(5000);
            myThread.interrupt();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
}

参考JDK文档可知,调用interrupt终止线程可分为以下几种情况:
a> 如果线程被阻塞在wait/sleep/join等方法调用,调用interrupt方法,线程内部会接受到InterruptedException
b> 如果线程被阻塞在基于interruptible channel实现的I/O操作,调用interrrupt方法,通道会被关闭,线程内部会接收到ClosedByInterruptException.
c> 如果线程被阻塞在Selector,selection操作会立刻返回,且返回值非负。
d> 除了上述情况,其他情况Interrupt只会简单是设置中断标识位。

从上面描述可知,这里需要解决两个问题:
对于没有阻塞的方法,使用interrupt如何终止线程;
对于没有基于interruptible channel实现的I/O操作,如果终止线程。

看第一个问题:终止没有阻塞的线程。
对于这类线程,调用interrupt,线程内部不会受到相应异常,只是设置了中断标识,这时候可以通过检测中断标识位,判断是否终止线程。

public class MyThread implements Runnable
{
    private Thread runner = new Thread(this, "runner");

    public void start()
    {
        runner.start();
    }

    public void interrupt()
    {
        runner.interrupt();
    }


    @Override
    public void run()
    {
        while(true)
        {
            if(runner.isInterrupted())
            {
                System.out.println("thread exit.");
                break;
            }
        }
    }

    public static void main(String[] args) 
    {
        MyThread myThread = new MyThread();     

        try
        {
            myThread.start();
            Thread.sleep(5000);
            myThread.interrupt();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
}

第二个问题:终止没有基于interruptible channel实现的I/O操作。这时候Interrupt方法就无能为力,可以尝试使用Thread.stop,关于stop的使用下面详细讨论

使用stop方法终止线程

使用stop终止线程,线程会被立刻终止掉。如下面代码所示

public class MyThread implements Runnable
{
    private Thread runner = new Thread(this, "runner");

    public void start()
    {
        runner.start();
    }

    public void stop()
    {
        runner.stop();
    }

    public void interrupt()
    {
        runner.interrupt();
    }


    @Override
    public void run()
    {
        try
        {
            while(true)
            {
                int a = 1;
            }
        }
        catch(Throwable e)
        {
            System.out.println(e);
        }
    }

    public static void main(String[] args) 
    {
        MyThread myThread = new MyThread();     

        try
        {
            myThread.start();
            Thread.sleep(5000);
            myThread.stop();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }
}

Stop方法现在已经不推荐使用了。详见Java的官方文档,对于不建议使用stop等方法的说明:

http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html

主要原因是,使用stop后,线程会立即是否所持有的锁。使用锁的本意通常是为了同步,保证数据的一致性。如果从A账户向B账户转账100元,如果在中间释放了锁,其他线程继续读取数据,很可能出现数据部一致。

public synchronized void ProcessData(int data)
    {
        accountA = accountA - data;
        accountB = accountB + data;
    }

另外,使用stop,线程会受到ThreadDeath的异常,而且这个异常可能发生在任何时候,包括catch和finally。

那么使用stop是否可以终止任何线程?其实不是这样,如下面代码所示:

public class MyThread extends Thread
{   
    @Override
    public synchronized void run()
    {
        try
        {
            while(true)
            {
                int a = 1;
            }
        }
        catch(Throwable e)
        {
            System.out.println(e);
        }
    }

    public static void main(String[] args) 
    {
        MyThread myThread = new MyThread();     

        try
        {
            myThread.start();
            Thread.sleep(5000);
            myThread.stop();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }       
    }
}

查看Thread.stop的源码可以发现,stop是一个同步方法,需要获取到一个同步锁,以为要线程run方法已占用的了锁,且不释放,所以stop方法一样也无法终止。附Thread.stop源码:

/**
     * @deprecated Method stop is deprecated
     */

    public final synchronized void stop(Throwable throwable)
    {
        if(throwable == null)
            throw new NullPointerException();
        SecurityManager securitymanager = System.getSecurityManager();
        if(securitymanager != null)
        {
            checkAccess();
            if(this != currentThread() || !(throwable instanceof ThreadDeath))
                securitymanager.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
        }
        if(threadStatus != 0)
            resume();
        stop0(throwable);
    }

终止I/O读写的线程:待补充
终止线程池中的线程:待补充
线程创建于任务执行的分离:待补充

参考资料:

http://forward.com.au/javaProgramming/HowToStopAThread.html

http://kdisk-sina-com.iteye.com/blog/835890

http://www.javacoffeebreak.com/articles/network_timeouts/