API 停止线程的方法
public final synchronized void stop(Throwable obj) ;
public final void stop() ;
public void destroy() ;
这三个方法都是Thread自带停止或者销毁的方法,不过都被废弃掉了。
为什么会被废弃呢?
* @deprecated This method was originally designed to force a thread to stop
* and throw a {@code ThreadDeath} as an exception. It was inherently unsafe.
* Stopping a thread with
* Thread.stop causes it to unlock all of the monitors that it
* has locked (as a natural consequence of the unchecked
* <code>ThreadDeath</code> exception propagating up the stack). If
* any of the objects previously protected by these monitors were in
* an inconsistent state, the damaged objects become visible to
* other threads, potentially resulting in arbitrary behavior. Many
* uses of <code>stop</code> should be replaced by code that simply
* modifies some variable to indicate that the target thread should
* stop running. The target thread should check this variable
* regularly, and return from its run method in an orderly fashion
* if the variable indicates that it is to stop running. If the
* target thread waits for long periods (on a condition variable,
* for example), the <code>interrupt</code> method should be used to
* interrupt the wait.
这是stop方法官方注释,大概意思就是:用这个方法停止线程,会出现线程不安全问题。当多个线程访问共享数据时,由于线程被stop了,没有来得及做收尾工作产生脏数据,那么共享数据就有可能被破坏,而这个破坏对于其他线程来说是可见的。而且外部直接通过stop来停止线程,过于暴力,例如我需要得到回调,但是被stop了,根本没有机会stop
既然不能直接暴力地停止线程,要给线程留出收尾的时间,那么我们可以在外部通知线程结束,至于是否结束由线程本身决定。
1.interrupt停止线程
public void interrupt()
(1)这是Thread 实例方法,中断线程;
(2)当线程调用了sleep(),wait(),join()处于阻塞等待,外部interrupt方法,那么线程内部会抛出InterruptedException,在catch异常以后做些以收尾工作结束线程;
(3)如果线程没有因为以上三种方法处于阻塞状态,外部调用其interrupt方法,线程是不会有任何影响的;
场景1:线程sleep10 * 1000毫秒以后,对变量进行加10操作;
模拟代码如下:
开启一个TestThread,休眠10秒对变量data加10,主线程在开启TestThread两秒钟后,调用interrupt中断线程。
public void testThread(){
final TestThread testThread = new TestThread();
testThread.start();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
testThread.interrupt();
}
}, 2 * 1000);//模拟两秒钟后中断线程
}
class TestThread extends Thread {
private int data;
@Override
public void run() {
super.run();
try {
sleep(10 * 1000);
data += 10;//进行运算
Log.d("test", "data:" + data);
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("test", "data:" + data);
}
}
}
打印日志如下:
从日志中可以看出,线程捕获到InterruptedException异常,变量data并没有被修改,线程任务结束退出。
2020-08-26 11:02:44.413 8969-10530/com.yang.memorytest W/System.err: java.lang.InterruptedException
2020-08-26 11:02:44.414 8969-10530/com.yang.memorytest W/System.err: at java.lang.Object.wait(Native Method)
2020-08-26 11:02:44.414 8969-10530/com.yang.memorytest W/System.err: at java.lang.Object.wait(Object.java:442)
2020-08-26 11:02:44.414 8969-10530/com.yang.memorytest W/System.err: at java.lang.Thread.join(Thread.java:1430)
2020-08-26 11:02:44.415 8969-10530/com.yang.memorytest W/System.err: at com.yang.memorytest.MainActivity$TestThread.run(MainActivity.java:96)
2020-08-26 11:02:44.415 8969-10530/com.yang.memorytest D/test: data:0
场景2:当线程任务是循环做业务处理,外部调用interrupt方法,循环条件需要增加判断是否被中断,是,则结束线程。
模拟代码如下:
同样开启一个TestThread,对变量data运算,如果data大于等于Integer.MAX_VALUE或者isInterrupted()为true,则跳出循环。主线程在开启TestThread两秒钟后,调用interrupt中断线程。
public void testThread() {
final TestThread testThread = new TestThread();
testThread.start();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
testThread.interrupt();
}
}, 2 * 1000);//模拟两秒钟后中断线程
}
class TestThread extends Thread {
private int data;
@Override
public void run() {
super.run();
while (data < Integer.MAX_VALUE&&!isInterrupted()) {
data += 10;//进行运算
}
Log.d("test", "data:" + data);
}
}
打印日志如下:
从日志中可以看出,isInterrupted()返回true,循环结束,线程退出。
2020-08-26 12:08:48.469 12078-13614/com.yang.memorytest D/test: isInterrupted():true
2020-08-26 12:08:48.469 12078-13614/com.yang.memorytest D/test: data:9667354
场景3:当线程任务是执行耗时任务,也是可以通过判断isInterrupted来决定是否结束任务。
class TestThread extends Thread {
@Override
public void run() {
super.run();
//业务处理1
if (isInterrupted()) {
return;
}
//业务处理2
if (isInterrupted()) {
return;
}
//业务处理3
if (isInterrupted()) {
return;
}
}
}
2.boolean变量停止线程
该方案与判断isInterrupted是否为true类似。
interrupt底层也是通过设置标记位,而且还加锁保证线程安全,所以boolean变量也要考虑线程安全问问题。
以下TestThread 暴露isStop变量给外部停止线程,大家注意到,变量是被volatile修饰的,因为当多个线程同时需要停止该线程时,每个线程都有自己的内存模型,会导致这个变量不可预知,所以volatile保证修改对于所有线程都是可见的。
class TestThread extends Thread {
private int data;
volatile public boolean isStop;
@Override
public void run() {
super.run();
while (data < Integer.MAX_VALUE&&!isStop) {
data += 1;//进行运算
}
Log.d("test", "isStop:" +isStop);
Log.d("test", "data:" + data);
}
}
3.以上两种方案对比
(1)interrupt可运用在sleep、wait、join等情况下,而boolean变量不行;
(2)interrupt涉及到jni的调用,而boolean变量只是Java层的;
5.isInterrupted()与interrupted()区别
共同点:两者都是获取中断状态;
isInterrupted是实例方法;调用interrupt()以后,如果中断状态没有被清理(catch InterruptedException或者调用interrupted),isInterrupted()会一直返回true;
public boolean isInterrupted()
interrupted是静态方法;调用interrupt()以后,调用interrupted第一次会返回true,以后就会返回false,因为中断状态被重置了。
public static native boolean interrupted()
6.InterruptedException
查看sleep注释发现,当抛出InterruptedException时,中断状态会被清空,也就是置为false。如果外部想在中断以后,获取到中断状态,建议在catch InterruptedException的时候,再次调用interrupted()方法。
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
// BEGIN Android-changed: Implement sleep() methods using a shared native implementation.
public static void sleep(long millis) throws InterruptedException
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论