HttpUrlConnection与HttpClient的认识(七) -HttpClient的线程安全问题
2017-04-07 11:10
495 查看
对于HttpClient的使用,我们在项目中一般会封装成一个工具栏使用,方便调用,在 HttpUrlConnection与HttpClient的认识(四) -HttpClient的封装 这篇博客中,已经说明过了。
上次的封装是没有问题的,我们拿来测试一下。
1、线程安全的封装
开启多线程使用一下这个类:
我们可以访问一下是完全没有问题的。
实际在我们的项目中,会经常将HttpClient封装成单例的形式,因为我们在多处需要进行HTTP通信发送网络请求时,没必要为每个请求都创建一个新的HttpClient,一个HttpClient就可以了。
但是在多线程情况下访问是出问题的,那我们看看这种情况:
2.线程不安全的封装
在测试的TestHttpClientThreadPro类中修改为
运行代码:
我们发现代码报错了,说明这是线程不安全的一种封装。。
针对这种情况,HttpClient提供了MultiThreadedHttpConnectionManager解决这种线程不安全的请求。使用起来很简单:
这样封装的httpClient使用起来就没问题了。
3.MultiThreadedHttpConnectionManager解决线程安全的问题
在测试的TestHttpClientThreadPro类中修改为
运行代码没有任何问题。
上次的封装是没有问题的,我们拿来测试一下。
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(); } }
开启多线程使用一下这个类:
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); } }); } } }
我们可以访问一下是完全没有问题的。
实际在我们的项目中,会经常将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(); } }
在测试的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)
我们发现代码报错了,说明这是线程不安全的一种封装。。
针对这种情况,HttpClient提供了MultiThreadedHttpConnectionManager解决这种线程不安全的请求。使用起来很简单:
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); HttpClient httpClient= new HttpClient(connectionManager);
这样封装的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(); } }
在测试的TestHttpClientThreadPro类中修改为
HttpClientUtil3.httpClientGet(url);
运行代码没有任何问题。
相关文章推荐
- HttpUrlConnection与HttpClient的认识(七) -HttpClient的线程安全问题
- HttpUrlConnection与HttpClient的认识(二)-请求头信息的问题
- HttpUrlConnection与HttpClient的认识(二)-请求头信息的问题
- HttpUrlConnection与HttpClient的认识(一)-HttpUrlConnection的使用
- Httpclient的会话保持引起的线程安全问题
- HttpUrlConnection,HttpClient,okttp和volley、xUtils Async-http Retrofit,RXJava的认识
- Java客户端HttpClient和HttpURLConnection修改请求头Host问题
- Java客户端HttpClient和HttpURLConnection修改请求头Host问题
- HttpUrlConnection与HttpClient的认识(三)-HttpClient的使用
- HttpUrlConnection与HttpClient的认识(六) -实际应用之刷网络流量
- RxJava + httpURLConnectionde 的简单测试demo,可以拿来处理一般的android访问网络的线程问题
- HttpUrlConnection与HttpClient的认识(三)-HttpClient的使用
- HttpUrlConnection与HttpClient的认识(四) -HttpClient的封装
- HttpUrlConnection与HttpClient的认识(四) -HttpClient的封装
- HttpUrlConnection与HttpClient的认识(一)-HttpUrlConnection的使用
- HttpURLConnection还是HttpClient?这是个问题!
- 解决HttpUrlConnection乱码问题
- Android网络连接之HttpURLConnection和HttpClient
- Java HttpURLConnection超时问题
- 使用HttpURLConnection做代理时遇到的cookie放不进去的问题