我试图解决同样的问题。我写了一个 HTTP Request Runnable,它需要 20 秒超时并在一个线程(第一个线程)上执行。我在一个单独的线程(第二个线程)上写了一个 Timeout Runnable,它应该在 10 秒后中断/停止第一个线程。在主线程中的每一秒(总共 3 个线程),我打印了线程的状态,发现第一个线程一直在运行。
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class A_Test_Thread_Interrupt {
public static final String URL_THAT_CAUSES_CONNECTION_TIMED_OUT_AFTER_20_SECONDS = "https://..."; // Need to provide this for your system. Must timeout after 20 seconds
// java.net.ConnectException: Connection timed out: connect
// at java.net.DualStackPlainSocketImpl.connect0(Native Method)
// at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
// at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
// at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
// at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
// at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
// at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
// at java.net.Socket.connect(Socket.java:589)
// at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:542)
// at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:414)
// at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
// at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:326)
// at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:610)
// at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)
// at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
// at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
// at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
// at A_Test_Thread_Interrupt$HttpRequestRunnable.run(A_Test_Thread_Interrupt.java:35)
// at java.lang.Thread.run(Thread.java:748)
public static void main(String[] args) {
System.out.println("main Start");
long startTime = System.currentTimeMillis();
SharedData sd = new SharedData();
System.out.println("Starting HTTPS Request Thread");
HttpRequestRunnable httpRequestRunnable = new HttpRequestRunnable(sd);
Thread workerThread = new Thread(httpRequestRunnable);
workerThread.start();
// workerThread.stop();
System.out.println("Starting Timeout Thread");
TimeoutRunnable timeoutRunnable = new TimeoutRunnable(workerThread, httpRequestRunnable, sd);
Thread timeoutThread = new Thread(timeoutRunnable);
timeoutThread.start();
for(int i=0; i<30; i++) {
System.out.println("workerThread.getState():"+workerThread.getState()+" timeoutThread.getState():"+timeoutThread.getState());
try { // Sleep for 1 second
Thread.sleep(1*1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
long endTime = System.currentTimeMillis();
System.out.println("main End timeInSeconds:"+(endTime-startTime)/1000);
}
public static class HttpRequestRunnable implements Runnable {
SharedData sharedData;
public HttpRequestRunnable (SharedData sharedData) {
this.sharedData = sharedData;
sharedData.setIsFinished(this, false);
}
public void run() {
System.out.println("Running HttpRequestRunnable Runner Start");
long startTime = System.currentTimeMillis();
try {
HttpGet httpGet = new HttpGet(URL_THAT_CAUSES_CONNECTION_TIMED_OUT_AFTER_20_SECONDS);
CloseableHttpResponse response = new DefaultHttpClient().execute(httpGet);
String responseString = EntityUtils.toString(response.getEntity());
System.out.println("responseString:"+responseString);
sharedData.setIsFinished(this, true);
} catch (Exception ex) {
ex.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("Running HttpRequestRunnable Runner End timeInSeconds:"+(endTime-startTime)/1000);
}
}
public static class SharedData {
Map<Runnable, Boolean> isFinishedMap = Collections.synchronizedMap(new LinkedHashMap<Runnable, Boolean>());
public void setIsFinished(Runnable runnable, Boolean value) {
synchronized(isFinishedMap) {
isFinishedMap.put(runnable, value);
}
}
public Boolean getIsFinished(Runnable runnable) {
synchronized(isFinishedMap) {
return isFinishedMap.get(runnable);
}
}
}
public static class TimeoutRunnable implements Runnable {
Thread workerThreadToKillIfItTakesTooLong;
Runnable runnable;
SharedData sharedData;
public TimeoutRunnable(Thread workerThreadToKillIfItTakesTooLong, Runnable runnable, SharedData sharedData) {
this.workerThreadToKillIfItTakesTooLong = workerThreadToKillIfItTakesTooLong;
this.runnable = runnable;
this.sharedData = sharedData;
}
public void run() {
System.out.println("Running TimeoutRunnable Runnable Start");
long startTime = System.currentTimeMillis();
int secondsToSleep = 1;
int iterationsPerNote = 2;
int iterations = 10;
for(int i=0; i<iterations; i++) { // Sleep for at most 10 seconds
boolean workerRunnableIsFinished = sharedData.getIsFinished(runnable);
if(workerRunnableIsFinished) {
return;
} else {
if(i % iterationsPerNote == 0) { // Print a message every 2 iterations
System.out.println("TimeoutRunnable waiting for workerRunnable...");
}
}
try { // Sleep for 1 second
Thread.sleep(secondsToSleep*1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 5 minutes have passed, kill thread
System.out.println("Killing worker thread for taking too long");
workerThreadToKillIfItTakesTooLong.stop();
sharedData.setIsFinished(runnable, true);
long endTime = System.currentTimeMillis();
System.out.println("Running TimeoutRunnable Runnable End timeInSeconds:"+(endTime-startTime)/1000);
}
}
}
这是输出
main: main Start
main: Starting HTTPS Request Thread
main: Starting Timeout Thread
1st_: Running HttpRequestRunnable Runner Start
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: Running TimeoutRunnable Runnable Start
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():TIMED_WAITING
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():TIMED_WAITING
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():TIMED_WAITING
2nd_: Killing worker thread for taking too long
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: Running TimeoutRunnable Runnable End timeInSeconds:10
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():RUNNABLE timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: main End timeInSeconds:30
我使用 SharedData 类来保存两个线程所需的信息。通过添加终止线程所需的额外信息(即 HttpGet),我可以让监控线程执行实际终止线程所需的任何操作(即调用 httpGet.abort)。这是修改后的代码(修改后的行以 // MOD 结尾)
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class A_Test_Thread_Interrupt {
public static final String public static final String URL_THAT_CAUSES_CONNECTION_TIMED_OUT_AFTER_20_SECONDS = "https://..."; // Need to provide this for your system. Must timeout after 20 seconds
// java.net.ConnectException: Connection timed out: connect
// at java.net.DualStackPlainSocketImpl.connect0(Native Method)
// at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
// at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
// at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
// at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
// at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
// at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
// at java.net.Socket.connect(Socket.java:589)
// at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:542)
// at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:414)
// at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
// at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:326)
// at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:610)
// at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)
// at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
// at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
// at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
// at A_Test_Thread_Interrupt$HttpRequestRunnable.run(A_Test_Thread_Interrupt.java:35)
// at java.lang.Thread.run(Thread.java:748)
public static void main(String[] args) {
System.out.println("main Start");
long startTime = System.currentTimeMillis();
SharedData sd = new SharedData();
System.out.println("Starting HTTPS Request Thread");
HttpRequestRunnable httpRequestRunnable = new HttpRequestRunnable(sd);
Thread workerThread = new Thread(httpRequestRunnable);
workerThread.start();
// workerThread.stop();
System.out.println("Starting Timeout Thread");
TimeoutRunnable timeoutRunnable = new TimeoutRunnable(workerThread, httpRequestRunnable, sd);
Thread timeoutThread = new Thread(timeoutRunnable);
timeoutThread.start();
for(int i=0; i<30; i++) {
System.out.println("workerThread.getState():"+workerThread.getState()+" timeoutThread.getState():"+timeoutThread.getState());
try { // Sleep for 1 second
Thread.sleep(1*1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
long endTime = System.currentTimeMillis();
System.out.println("main End timeInSeconds:"+(endTime-startTime)/1000);
}
public static class HttpRequestRunnable implements Runnable {
SharedData sharedData;
public HttpRequestRunnable (SharedData sharedData) {
this.sharedData = sharedData;
sharedData.setIsFinished(this, false);
}
public void run() {
System.out.println("Running HttpRequestRunnable Runner Start");
long startTime = System.currentTimeMillis();
try {
HttpGet httpGet = new HttpGet(URL_THAT_CAUSES_CONNECTION_TIMED_OUT_AFTER_20_SECONDS);
sharedData.setHttpGet(this, httpGet); // MOD
CloseableHttpResponse response = new DefaultHttpClient().execute(httpGet);
String responseString = EntityUtils.toString(response.getEntity());
System.out.println("responseString:"+responseString);
sharedData.setIsFinished(this, true);
} catch (Exception ex) {
ex.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("Running HttpRequestRunnable Runner End timeInSeconds:"+(endTime-startTime)/1000);
}
}
public static class SharedData {
Map<Runnable, Boolean> isFinishedMap = Collections.synchronizedMap(new LinkedHashMap<Runnable, Boolean>());
Map<Runnable, HttpGet> httpGetMap = Collections.synchronizedMap(new LinkedHashMap<Runnable, HttpGet>()); // MOD
public void setIsFinished(Runnable runnable, Boolean value) {
synchronized(isFinishedMap) {
isFinishedMap.put(runnable, value);
}
}
public Boolean getIsFinished(Runnable runnable) {
synchronized(isFinishedMap) {
return isFinishedMap.get(runnable);
}
}
public void setHttpGet(Runnable runnable, HttpGet httpGet) { // MOD
synchronized(httpGetMap) { // MOD
httpGetMap.put(runnable, httpGet); // MOD
} // MOD
} // MOD
public HttpGet getHttpGet(Runnable runnable) { // MOD
synchronized(httpGetMap) { // MOD
return httpGetMap.get(runnable); // MOD
} // MOD
} // MOD
}
public static class TimeoutRunnable implements Runnable {
Thread workerThreadToKillIfItTakesTooLong;
Runnable runnable;
SharedData sharedData;
public TimeoutRunnable(Thread workerThreadToKillIfItTakesTooLong, Runnable runnable, SharedData sharedData) {
this.workerThreadToKillIfItTakesTooLong = workerThreadToKillIfItTakesTooLong;
this.runnable = runnable;
this.sharedData = sharedData;
}
public void run() {
System.out.println("Running TimeoutRunnable Runnable Start");
long startTime = System.currentTimeMillis();
int secondsToSleep = 1;
int iterationsPerNote = 2;
int iterations = 10;
for(int i=0; i<iterations; i++) { // Sleep for at most 10 seconds
boolean workerRunnableIsFinished = sharedData.getIsFinished(runnable);
if(workerRunnableIsFinished) {
return;
} else {
if(i % iterationsPerNote == 0) { // Print a message every 2 iterations
System.out.println("TimeoutRunnable waiting for workerRunnable...");
}
}
try { // Sleep for 1 second
Thread.sleep(secondsToSleep*1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 5 minutes have passed, kill thread
System.out.println("Killing worker thread for taking too long");
workerThreadToKillIfItTakesTooLong.stop();
HttpGet httpGet = sharedData.getHttpGet(runnable); // MOD
httpGet.abort(); // MOD
sharedData.setIsFinished(runnable, true);
long endTime = System.currentTimeMillis();
System.out.println("Running TimeoutRunnable Runnable End timeInSeconds:"+(endTime-startTime)/1000);
}
}
}
这是修改后的输出
main: main Start
main: Starting HTTPS Request Thread
main: Starting Timeout Thread
1st_: Running HttpRequestRunnable Runner Start
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: Running TimeoutRunnable Runnable Start
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():TIMED_WAITING
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: TimeoutRunnable waiting for workerRunnable...
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: Killing worker thread for taking too long
main: workerThread.getState():RUNNABLE timeoutThread.getState():RUNNABLE
2nd_: Running TimeoutRunnable Runnable End timeInSeconds:10
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: workerThread.getState():TERMINATED timeoutThread.getState():TERMINATED
main: main End timeInSeconds:30