一、如何正確的停止線程
使用interrupt來通知,而不是強制。
interrupt,即中斷,需要停止線程時,需要另外一個線程向該線程通知你該中斷了,而何時中斷,停不停止,是由該線程自己決定的,外部並沒有方法直接操作它。
線程何時會停止:
- run()方法中的代碼都已經執行完畢了。
- 線程進行業務邏輯處理時拋出異常了。
二、使用interrupt停止線程
1.普通情況下停止線程
先嚐試使用thread.interrupt()中斷該線程。
/**
* 普通情況下停止線程
*/
public class StopThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Work());
thread.start();
// 線程啓動後1s,通知中斷
Thread.sleep(1000);
thread.interrupt();
}
}
// 任務:打印出小於999999999的所有1000的倍數的數
class Work implements Runnable{
@Override
public void run() {
for(int num=0; num < 999999999; num ++){
if(num % 1000 == 0){
System.out.println(num);
}
}
System.out.println("任務完成");
}
}
發現即使調用了thread.interrupt(),線程並沒有中斷。因爲線程中沒有對這個中斷進行響應,需要在線程中添加一些響應中斷的代碼:
// 任務:打印出小於999999999的所有1000的倍數的數
class Work implements Runnable{
@Override
public void run() {
for(int num=0; num < 999999999; num ++){
// 判斷中斷
if(Thread.currentThread().isInterrupted()){
break;
}
if(num % 1000 == 0){
System.out.println(num);
}
}
System.out.println("任務完成");
}
}
2.在阻塞的情況下停止線程
線程在sleep阻塞時,收到主線程發來的中斷通知:
/**
* 線程中有sleep阻塞的情況下停止線程
*/
public class StopThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Work());
thread.start();
// 線程啓動後1s,通知中斷
Thread.sleep(500);
thread.interrupt();
}
}
// 任務:打印出小於300的數
class Work implements Runnable{
@Override
public void run() {
for(int num=0; num < 300; num ++){
// 判斷中斷
if(Thread.currentThread().isInterrupted()){
break;
}
System.out.println(num);
}
// 阻塞等待其他業務邏輯
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任務完成");
}
}
會發現拋出了異常,即sleep時收到中斷
3.在每次迭代後都阻塞的情況下停止線程
每次迭代都有sleep阻塞,而阻塞的特點就是它本身就能響應中斷而拋出異常去停止線程,這種情況下我們就不需要手動去寫響應中斷的代碼了。
/**
* 線程中每次循環都會有sleep阻塞的情況下停止線程
*/
public class StopThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Work());
thread.start();
// 線程啓動後1s,通知中斷
Thread.sleep(2000);
thread.interrupt();
}
}
// 任務:打印出小於10000的數
class Work implements Runnable{
@Override
public void run() {
try {
for(int num=0; num < 10000; num ++){
System.out.println(num);
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任務完成");
}
}
4.sleep會清除中斷標誌的問題
如果 try{ sleep }catch 放在循環內,當sleep響應中斷後,會清除中斷標誌位,所以即使我們使用Thread.currentThread().isInterrupted()來手動響應中斷,也不會停止線程。
// 任務:打印出小於10000的數
class Work implements Runnable{
@Override
public void run() {
for(int num=0; num < 10000; num ++){
// 手動響應
if(Thread.currentThread().isInterrupted()){
break;
}
System.out.println(num);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任務完成");
}
}