線程,是編程中處理并發(fā)最常用的方式。但是如果用不好,也是一個比較麻煩的事情。畢竟創(chuàng)建一個線程是件容易的事情。但是怎么終止一個線程,卻不那么容易。關(guān)于線程的退出,最好的方式當然是讓線程處理完相應(yīng)的工作后自動退出。其次是主線程有效的通知到工作線程,“Hello, please exit!”。當然,暴力終止是不被推薦的,可能會發(fā)生數(shù)據(jù)一致性的問題?;蛘撸渌粗獑栴}。
我首先能想到的通知一個線程中斷,就是設(shè)置線程中斷標志。
import java.io.IOException;
import java.net.ServerSocket;
public class InterruptTest {
private static boolean interrupt = false;
public static boolean getInterrupt(){
return interrupt;
}
public synchronized static void setInterupt(boolean value){
interrupt = value;
}
public void method1(){
try{
int i=1;
while(!interrupt){
System.out.println("thread continue excute " + i++);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public static void main(String args[]) throws InterruptedException, IOException{
InterruptTest test = new InterruptTest();
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
test.method1();
}
});
thread.start();
Thread.sleep(2000);
InterruptTest.setInterupt(true);
thread.join();
System.out.println("Main thread finished!");
}
}
這種方式當然可以有效的通知一直有任務(wù)運行的線程去中斷自己。但是,如果線程一直處于阻塞狀態(tài),比如Thread.sleep()。顯然線程是無法及時獲取中斷標志的??紤]下面這種場景。這個時候就該Thread.interrupt出場了。
import java.io.IOException;
import java.net.ServerSocket;
public class InterruptTest {
private static boolean interrupt = false;
public static boolean getInterrupt(){
return interrupt;
}
public synchronized static void setInterupt(boolean value){
interrupt = value;
}
public void method1(){
try{
int i=1;
while(!interrupt){
System.out.println("thread continue excute " + i++);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public void method2(){
try{
int i=1;
while(!interrupt){
System.out.println("thread continue excute " + i++);
Thread.sleep(3000);
}
}catch(Exception e){
setInterupt(true);
System.out.println(e.getMessage());
}
}
public static void main(String args[]) throws InterruptedException, IOException{
InterruptTest test = new InterruptTest();
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
test.method2();
}
});
thread.start();
Thread.sleep(2000);
thread.interrupt();
thread.join();
System.out.println("Main thread finished!");
}
}
輸出結(jié)果為:
thread continue excute 1
sleep interrupted
Main thread finished!
Thread.sleep函數(shù)導致線程阻塞。interrput函數(shù)會發(fā)送一個java.lang.InterruptedException異常,當線程捕獲這個異常就會通知線程終止循環(huán)。
但調(diào)用interrupt函數(shù)也僅僅只能讓能接收InterruptedException異常的阻塞終止。其他一些I/O阻塞是不會理會interrupt函數(shù)發(fā)出的異常的。這種情況就需要針對不同的情況做處理了。比如,socket阻塞在accpet函數(shù)。interrupt函數(shù)就無能為力了。這時候就可以通過socket的close函數(shù)來關(guān)閉端口。
import java.io.IOException;
import java.net.ServerSocket;
public class InterruptTest {
private static boolean interrupt = false;
private ServerSocket socket1;
public ServerSocket getSocket(){
return socket1;
}
public static boolean getInterrupt(){
return interrupt;
}
public synchronized static void setInterupt(boolean value){
interrupt = value;
}
public void method1(){
try{
int i=1;
while(!interrupt){
System.out.println("thread continue excute " + i++);
}
}catch(Exception e){
System.out.println(e.getMessage());
}
}
public void method2(){
try{
int i=1;
while(!interrupt){
System.out.println("thread continue excute " + i++);
Thread.sleep(3000);
}
}catch(Exception e){
setInterupt(true);
e.printStackTrace();
}
}
public void method3(){
try{
System.out.println("Start to run thread!");
socket1 = new ServerSocket(8889);
socket1.accept();
System.out.println("Setup one connection!");
}catch(Exception e){
setInterupt(true);
System.out.println(e.getMessage());
}
}
public static void main(String args[]) throws InterruptedException, IOException{
InterruptTest test = new InterruptTest();
Thread thread = new Thread(new Runnable(){
@Override
public void run() {
test.method3();
}
});
thread.start();
Thread.sleep(2000);
test.getSocket().close();
thread.join();
System.out.println("Main thread finished!");
}
}
輸出結(jié)果:
Start to run thread!
socket closed
Main thread finished!
其實在調(diào)用Socket.close函數(shù)的時候,socket線程會接收到一個java.net.SocketException異常。然后通過捕獲這個異常,改變線程中斷標志來中斷線程。
總之,如何有效的中斷一個線程。需要根據(jù)實際情況來處理。換到風險投資領(lǐng)域。進入之前就先想好退出方式。如果你的工作線程沒有阻塞,就用中斷標志控制就好。如果有一般阻塞,準備好處理interruptedException。如果有其他I/O阻塞,先提供阻塞對象的句柄獲取方式。然后準備好處理相應(yīng)的中斷異常。