How to cancel a thread making a http request from another thread

Viewed 2364

Any suggestion on how do we kill a thread which has made a http post request and waiting till getting response. I want to kill this thread from another thread running parallely before it could receive any response from request. I got suggestion to close or abort the http post request but did not workout, can anyone suggest how can we abort the http request in this scenario or any other way to achieve this.

3 Answers

I was trying to solve the same thing. I wrote an HTTP Request Runnable that took 20 seconds to timeout and executed on one thread (1st Thread). I wrote a Timeout Runnable on a separate thread (2nd Thread) that was supposed to interrupt/stop the 1st Thread after 10 seconds. For every second in the Main Thread (3 threads total), I printed the state of the threads and found the 1st thread kept running.

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);
        }

    }
}

Here was the output

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

I use a SharedData class to hold information needed by both threads. By adding the extra information needed to terminate the thread (ie the HttpGet), I can have the monitoring thread do whatever is necessary to actually terminate the thread (ie call httpGet.abort). This is the modified code (modified lines end with // 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);
        }

    }
}

Here was the modified output

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
Related