HttpUrlConnection与HttpClient的认识(七) -HttpClient的线程安全问题

来源:互联网 发布:男士护肤 知乎 旁氏 编辑:程序博客网 时间:2024/06/05 07:04


转载自:http://blog.csdn.net/u010248330/article/details/69524542


对于HttpClient的使用,我们在项目中一般会封装成一个工具栏使用,方便调用,在 HttpUrlConnection与HttpClient的认识(四) -HttpClient的封装 这篇博客中,已经说明过了。

上次的封装是没有问题的,我们拿来测试一下。

1、线程安全的封装

package com.httpClient.thread.test;import org.apache.commons.httpclient.*;import org.apache.commons.httpclient.methods.*;import java.io.*;/** *线程安全的封装,因为每次都是重新实例化的HttpClient */public class HttpClientUtil1 {    public static String httpClientGet(String url){        StringBuilder sb =new StringBuilder();        //每次都重新实例化一个httpClient        HttpClient httpClient = new HttpClient();        GetMethod getMethod = new GetMethod(url);        try {             httpClient.executeMethod(getMethod);             InputStream is = getMethod.getResponseBodyAsStream();             BufferedReader dis=new BufferedReader(new InputStreamReader(is,"utf-8"));                String str ="";                                        while((str =dis.readLine())!=null){                 sb.append(str);             }        } catch (Exception e) {             e.printStackTrace();        } finally {            // 关闭连接            getMethod.releaseConnection();        }        return sb.toString();     }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

开启多线程使用一下这个类:

package com.httpClient.thread.test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestHttpClientThreadPro {     private static String url="http://www.baidu.com";     public static void main(String[] args) {         ExecutorService threadPool=Executors.newFixedThreadPool(4);         for ( int i = 0; i < 6; i++) {             threadPool.execute(new Runnable() {                @Override                public void run() {                        HttpClientUtil1.httpClientGet(url);                        System.out.println("=====访问结果==="+result);            }         });         }     }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

我们可以访问一下是完全没有问题的。

实际在我们的项目中,会经常将HttpClient封装成单例的形式,因为我们在多处需要进行HTTP通信发送网络请求时,没必要为每个请求都创建一个新的HttpClient,一个HttpClient就可以了。 
但是在多线程情况下访问是出问题的,那我们看看这种情况:

2.线程不安全的封装

package com.httpClient.thread.test;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.methods.GetMethod;import org.apache.commons.httpclient.params.HttpMethodParams;/** *线程不安全,只有唯一的一个实例 */public class HttpClientUtil2 {    //唯一的HttpClient实例    private static HttpClient httpClient=new HttpClient();    public static String httpClientGet(String url){        StringBuilder sb =new StringBuilder();        GetMethod getMethod = new GetMethod(url);        try {            httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");            httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(3000);            httpClient.getHttpConnectionManager().getParams().setSoTimeout(3000);            //httpClient永远是同一个            int statusCode = httpClient.executeMethod(getMethod);            if (statusCode==200) {                InputStream is = getMethod.getResponseBodyAsStream();                BufferedReader dis=new BufferedReader(new InputStreamReader(is,"utf-8"));                   String str ="";                                           while((str =dis.readLine())!=null){                    sb.append(str);                    sb.append("\r\n");                 }               }        } catch (Exception e) {            e.printStackTrace();        }finally{            getMethod.releaseConnection();        }        return sb.toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在测试的TestHttpClientThreadPro类中修改为

      HttpClientUtil2.httpClientGet(url);

运行代码:

2017-4-7 10:48:25 org.apache.commons.httpclient.SimpleHttpConnectionManager getConnectionWithTimeout警告: SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.2017-4-7 10:48:25 org.apache.commons.httpclient.SimpleHttpConnectionManager getConnectionWithTimeout警告: SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.2017-4-7 10:48:25 org.apache.commons.httpclient.SimpleHttpConnectionManager getConnectionWithTimeout警告: SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.java.net.SocketException: Socket Closed    at java.net.SocketInputStream.socketRead0(Native Method)    at java.net.SocketInputStream.read(SocketInputStream.java:129)    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)    at java.io.BufferedInputStream.read(BufferedInputStream.java:237)    at org.apache.commons.httpclient.HttpParser.readRawLine(HttpParser.java:78)    at org.apache.commons.httpclient.HttpParser.readLine(HttpParser.java:106)    at org.apache.commons.httpclient.HttpConnection.readLine(HttpConnection.java:1116)    at org.apache.commons.httpclient.HttpMethodBase.readStatusLine(HttpMethodBase.java:1973)    at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1735)    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1098)    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:24)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)org.apache.commons.httpclient.ProtocolException: Unable to parse header: e8anpt    at org.apache.commons.httpclient.HttpParser.parseHeaders(HttpParser.java:202)    at org.apache.commons.httpclient.HttpMethodBase.readResponseHeaders(HttpMethodBase.java:1935)    at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1737)    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1098)    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:24)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)java.io.IOException: Stream closed    at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145)    at java.io.BufferedInputStream.fill(BufferedInputStream.java:189)    at java.io.BufferedInputStream.read(BufferedInputStream.java:237)    at org.apache.commons.httpclient.HttpParser.readRawLine(HttpParser.java:78)    at org.apache.commons.httpclient.HttpParser.readLine(HttpParser.java:106)    at org.apache.commons.httpclient.HttpConnection.readLine(HttpConnection.java:1116)    at org.apache.commons.httpclient.HttpMethodBase.readStatusLine(HttpMethodBase.java:1973)    at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1735)    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1098)    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:24)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)java.lang.IllegalStateException: Unexpected release of an unknown connection.    at org.apache.commons.httpclient.SimpleHttpConnectionManager.releaseConnection(SimpleHttpConnectionManager.java:225)    at org.apache.commons.httpclient.HttpConnection.releaseConnection(HttpConnection.java:1179)    at org.apache.commons.httpclient.HttpMethodBase.ensureConnectionRelease(HttpMethodBase.java:2430)    at org.apache.commons.httpclient.HttpMethodBase.responseBodyConsumed(HttpMethodBase.java:2422)    at org.apache.commons.httpclient.HttpMethodBase$1.responseConsumed(HttpMethodBase.java:1892)    at org.apache.commons.httpclient.AutoCloseInputStream.notifyWatcher(AutoCloseInputStream.java:198)    at org.apache.commons.httpclient.AutoCloseInputStream.checkClose(AutoCloseInputStream.java:170)    at org.apache.commons.httpclient.AutoCloseInputStream.read(AutoCloseInputStream.java:109)    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)    at java.io.InputStreamReader.read(InputStreamReader.java:167)    at java.io.BufferedReader.fill(BufferedReader.java:136)    at java.io.BufferedReader.readLine(BufferedReader.java:299)    at java.io.BufferedReader.readLine(BufferedReader.java:362)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:29)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)2017-4-7 10:48:25 org.apache.commons.httpclient.SimpleHttpConnectionManager getConnectionWithTimeout警告: SimpleHttpConnectionManager being used incorrectly.  Be sure that HttpMethod.releaseConnection() is always called and that only one thread and/or method is using this connection manager at a time.Exception in thread "pool-1-thread-1" java.lang.IllegalStateException: Unexpected release of an unknown connection.    at org.apache.commons.httpclient.SimpleHttpConnectionManager.releaseConnection(SimpleHttpConnectionManager.java:225)    at org.apache.commons.httpclient.HttpConnection.releaseConnection(HttpConnection.java:1179)    at org.apache.commons.httpclient.HttpMethodBase.ensureConnectionRelease(HttpMethodBase.java:2430)    at org.apache.commons.httpclient.HttpMethodBase.releaseConnection(HttpMethodBase.java:1186)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:37)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)java.lang.IllegalStateException: Connection is not open    at org.apache.commons.httpclient.HttpConnection.assertOpen(HttpConnection.java:1277)    at org.apache.commons.httpclient.HttpConnection.readLine(HttpConnection.java:1115)    at org.apache.commons.httpclient.HttpMethodBase.readStatusLine(HttpMethodBase.java:1973)    at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1735)    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1098)    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:24)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)org.apache.commons.httpclient.ProtocolException: Unable to parse header: T 0 K    at org.apache.commons.httpclient.HttpParser.parseHeaders(HttpParser.java:202)    at org.apache.commons.httpclient.HttpMethodBase.readResponseHeaders(HttpMethodBase.java:1935)    at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1737)    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1098)    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)    at com.httpClient.thread.test.HttpClientUtil2.httpClientGet(HttpClientUtil2.java:24)    at com.httpClient.thread.test.TestHttpClientThreadPro$1.run(TestHttpClientThreadPro.java:18)    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)    at java.lang.Thread.run(Thread.java:662)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121

我们发现代码报错了,说明这是线程不安全的一种封装。。

针对这种情况,HttpClient提供了MultiThreadedHttpConnectionManager解决这种线程不安全的请求。使用起来很简单:

MultiThreadedHttpConnectionManager connectionManager =                 new MultiThreadedHttpConnectionManager();          HttpClient httpClient= new HttpClient(connectionManager);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这样封装的httpClient使用起来就没问题了。

3.MultiThreadedHttpConnectionManager解决线程安全的问题

package com.httpClient.thread.test;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;import org.apache.commons.httpclient.methods.GetMethod;import org.apache.commons.httpclient.params.HttpConnectionManagerParams;import org.apache.commons.httpclient.params.HttpMethodParams;/** * 线程安全的封装 */public class HttpClientUtil3 {    private static MultiThreadedHttpConnectionManager connectionManager;    private static HttpClient httpClient;    private static int maxHostConnections = 10;    private static int maxTotalConnections = 20;    static {        connectionManager = new MultiThreadedHttpConnectionManager();        HttpConnectionManagerParams params = new HttpConnectionManagerParams();        params.setDefaultMaxConnectionsPerHost(maxHostConnections);        params.setMaxTotalConnections(maxTotalConnections);        connectionManager.setParams(params);        httpClient = new HttpClient(connectionManager);        httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");    }    public static String httpClientGet(String url){        StringBuilder sb =new StringBuilder();        GetMethod getMethod = new GetMethod(url);        try {            //httpClient永远是同一个            int statusCode = httpClient.executeMethod(getMethod);            if (statusCode==200) {                InputStream is = getMethod.getResponseBodyAsStream();                BufferedReader dis=new BufferedReader(new InputStreamReader(is,"utf-8"));                   String str ="";                                           while((str =dis.readLine())!=null){                    sb.append(str);                    sb.append("\r\n");                 }               }        } catch (Exception e) {            e.printStackTrace();        }finally{            getMethod.releaseConnection();        }        return sb.toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

在测试的TestHttpClientThreadPro类中修改为

      HttpClientUtil3.httpClientGet(url);

运行代码没有任何问题。


阅读全文
0 0
原创粉丝点击