停止线程的方式
在 Java 中停止线程的方式有三种:
1、线程的 run 方法执行到最后,线程正常结束并退出;
2、使用 Thread 类提供的 stop 方法去停止一个正在运行的线程,该方法由于存在安全问题已被标记为过时,不推荐使用了;
3、使用 Java 中推荐的方式,即使用线程中断机制,去中断线程;
三种方式介绍
1、run 方法自行结束,这个是线程正常执行完后结束,没什么值得说的;
2、Thread 类的 stop 方法;
这个是一个在 Java 中被标记为过时的方法,因为该方法有线程安全方面的风险,因此已经不推荐使用了;具体原因可以参考源码中的解释,已经写的非常清楚了,下面举个例子说明:
public static void main(String[] args) throws InterruptedException {
TestObject object = new TestObject(1, 1);
TestRunnable testRunnable = new TestRunnable(object);
Thread t1 = new Thread(testRunnable);
Thread t2 = new Thread(testRunnable);
t1.start();
t2.start();
// t1.stop();
t1.join();
t2.join();
System.out.println(object.getX() == object.getY());
System.out.println(object.getX() );
System.out.println(object.getY());
System.out.println("main end");
}
private static class TestObject{
private int x ;
private int y ;
TestObject(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
private static class TestRunnable implements Runnable {
private final Object lock = new Object();
private final TestObject object;
TestRunnable(TestObject object) {
this.object = object;
}
@Override
public void run() {
synchronized (lock){
object.setX(object.getX() + 1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
object.setY(object.getY() + 1);
}
}
}
结果
false
3
2
如上 demo,基本上可以说明 stop 的线程安全问题了。在上面的示例中,runnable 任务分别将对象 TestObject 中的属性 x 和属性 y 加1,中间休息了 100 毫秒,模拟执行其他操作,在正常情况下他们是同时加1的。在不调用 stop 方法时,两个属性结果一致,都是3,但是当调用线程 t1 调用了 stop 方法,属性 x 值加1了,由于 y 得值还没来得及加 1 线程停止了,导致本来是原子的操作两个属性,最后没能一起增加,导致了线程安全的问题。
因此 stop 方法在 Java 中标记为过时,并且也不推荐使用了。现在推荐的是使用线程的中断机制。
3、线程的中断机制介绍;
Java 中提供的线程中断机制,实际上是一种线程之间的协作方式;这种中断和 stop 有着本质的区别,stop 是直接中断线程,而 Java 中的中断机制,其实是给线程设置一个中断的标识,当前线程并不会被真的终止执行,因此需要线程自己去根据的标识处理相关逻辑。下面介绍线程中断机制中,三个重要的方法。
a、interrupt 方法
这个方法可以给线程设置中断的标识,然后可以根据中断的标识自行处理。例如线程 A 和主线程,主线程调用线程 A 的 interrupt 方法,这个时候就把线程 A 的中断标识设为了 true,但是这个时候线程 A 仍在执行,需要线程 A 根据标识做处理,结束自己的执行。
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
while(true) {
System.out.println("threadA is running");
//如果中断标识为true,则退出循环,threadA 执行结束
if (Thread.currentThread().isInterrupted()) {
System.out.println("threadA is stop");
break;
}
}
});
threadA.start();
Thread.sleep(100);
threadA.interrupt();
threadA.join();
System.out.println("main is end");
}
结果
threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is running
threadA is stop
main is end
在调用 interrupt 方法时,还有一个需要注意的,当线程内调用了 wait、sleep、join 方法而被挂起时,当前线程会在这些地方抛出,InterruptedException 异常后返回。例如如果线程 A 中有调用 wait 或者 sleep 或者 join 方法,这个时候主线程调用了 interrupt 方法,则线程 A 会在调用这些方法的地方,抛出 InterruptedException 异常,需要程序自己处理该异常,保证线程安全切正确的执行。
public static void main(String[] args) throws InterruptedException {
TestObject object = new TestObject(1, 1);
TestRunnable testRunnable = new TestRunnable(object);
Thread t1 = new Thread(testRunnable);
Thread t2 = new Thread(testRunnable);
t1.start();
t2.start();
t1.interrupt();
t1.join();
t2.join();
System.out.println(object.getX() == object.getY());
System.out.println(object.getX() );
System.out.println(object.getY());
System.out.println("main end");
}
private static class TestObject{
private int x ;
private int y ;
TestObject(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
private static class TestRunnable implements Runnable {
private final Object lock = new Object();
private final TestObject object;
TestRunnable(TestObject object) {
this.object = object;
}
@Override
public void run() {
synchronized (lock){
int x = object.getX();
int y = object.getY();
try {
object.setX(x + 1);
Thread.sleep(100);
object.setY(y + 1);
} catch (InterruptedException e) {
object.setY(y);
object.setX(x);
}
}
}
}
结果
true
2
2
上面示例使用了说明 stop 不安全的例子,稍微修改了代码,由于示例中有调用 sleep 方法,因此处理了 InterruptedException 异常,将 stop 改为了 interrupt 方法,从而避免了线程不安全的问题,该示例了没有对线程的中断标识做处理,因为该示例处理的意义不大,这个根据具体使用场景处理就行。
b、isInterrupted 方法
这个方法用于检查当前线程的中断标识,如果当前线程标识为中断,则返回 true 反之则返回 false,这个方法的使用方式,在上面的示例中已经给出,这里就不提供具体的代码了。
c、interrupted 方法
这个方法也是用于检查线程的中断标识,如果中断则返回 true,反之则是 false。但是和 isInterrupted 方法有两点不同;第一如果发现线程被中断会返回 true,同时会清除线程的中断标识,这个时候线程标识变成了非中断;第二这个方法获取的是当前执行线程的线程状态,并不是被调用线程的状态,具体看下面的代码;
/**
* Tests whether the current thread has been interrupted. The
* interrupted status of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return true
if the current thread has been interrupted;
* false
otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
上面是该方法的 JDK 源码,它返回的是当前执行的线程的中断状态;
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
System.out.println("threadA start interrupted :" + Thread.currentThread().isInterrupted());
while (!Thread.interrupted()) {
}
System.out.println("threadA stop interrupted :" + Thread.currentThread().isInterrupted());
});
threadA.start();
threadA.interrupt();
threadA.join();
System.out.println("main end");
}
结果
threadA start interrupted :true
threadA stop interrupted :false
main end
总结
1、stop 是线程不安全的方法,不推荐使用;
2、interrupt 只是设置一个中断的标识,需要在线程里面处理相关逻辑;
3、interrupt 执行时,有 sleep、wait、join、的地方会抛出 InterruptedException 异常;
4、interrupted 和 isInterrupted 都可以获取线程的中断状态;
5、interrupted 获取的是当前线程的中断状态,并且会清除中断标识;