项目开发中碰到的一个线程有关问题 (二)
项目开发中碰到的一个线程问题 (二)
前几天碰到了一个问题项目开发中碰到的一个线程问题(一),当时说的不太清楚
,这里好好总结下。
现象:多线程Http请求,在服务端发现总会有相同参数的请求。当时认为是HttpClient在多线程下是线程非安全的,wangzhangxing 提到:
事实上代码里也确实是这么做的:
自己又认真狠看了几遍代码,其中的调用顺序是这样的,DataServiceImpl 是具体的业务实现类,通过HttpHelper对象获取服务端的信息。
HttpHelper代码,其中有些方法省略,详细代码可以下载附件查看
从上面代码可以看到,HttpHelper对象是DataServiceImpl 的全局变量,虽然HttpHelperHttpClient是通过PoolingClientConnectionManager 获取HttpClient的,没有进行同步操作,在多线程就很可能会出现获取出来的对象是相同的,(个人理解),这里抛砖引玉,哪位牛人帮详解下,^_^。
那接下来怎么修改呢,很简单,只修改HttpHelper中的HttpClient变量使用范围:
有几点自己以后要注意:
1、要注意代码细节,多测试才能发现问题。
2、程序引用的代码即使不能钻研的很明白,至少能理解使用的具体场合。
3、最重要的一点,以后要恶补基础啦, 三天不学习,不知道南北极哪,
前几天碰到了一个问题项目开发中碰到的一个线程问题(一),当时说的不太清楚
现象:多线程Http请求,在服务端发现总会有相同参数的请求。当时认为是HttpClient在多线程下是线程非安全的,wangzhangxing 提到:
引用
PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
cm.setMaxTotal(100);
然后new DefaultHttpClient(cm));
这样出来的client就是线程安全的了
。cm.setMaxTotal(100);
然后new DefaultHttpClient(cm));
这样出来的client就是线程安全的了
事实上代码里也确实是这么做的:
/** * 初始化HttpClient对象 * * @return HttpClient对象 */ private HttpClient getHttpClient() { HttpClient httpClient = null; try { // ThreadSafeClientConnManager\PoolingClientConnectionManager PoolingClientConnectionManager pccm = new PoolingClientConnectionManager(getSchemeRegistry()); // pccm.setDefaultMaxPerRoute(20); httpClient = new DefaultHttpClient(pccm, prepareHttpParams()); } catch (Exception e) { e.printStackTrace(); httpClient = new DefaultHttpClient(); } return httpClient; }
自己又认真狠看了几遍代码,其中的调用顺序是这样的,DataServiceImpl 是具体的业务实现类,通过HttpHelper对象获取服务端的信息。
package org.okura.timer; /** * 服务接口实现类 * * @author okura * @since 2012-10-10 */ public class DataServiceImpl implements IDataService { private HttpHelper httpHelper; public DataServiceImpl() { httpHelper = new HttpHelper(); } @Override public void getProduct(String type, String date) { // System.out.println(Thread.currentThread().getName() + // "---DataServiceImpl---type:" + type +",date:" + date); httpHelper.requestString(HttpUrlUtil.getProductUrl(type, date)); // System.out.println("type:" + type +",date:" + date + "--" + response); } }
HttpHelper代码,其中有些方法省略,详细代码可以下载附件查看
public class HttpHelper { /** Http请求方法对象 */ private HttpRequestBase request; /** * 发送Http请求,返回文本形式的请求响应 * * @param url url地址 * @return */ public String requestString(String url) { try { HttpResponse stringResponse = send(url); // 判断请求是否成功 if (!isResponseSuccessStatus(stringResponse)) { return null; } System.out.println(Thread.currentThread().getName() + "---url:" + url); return parseHttpResponse2String(stringResponse); } catch (Exception e) { e.printStackTrace(); System.out.println("requestString()" + url); return null; } } /** * 发送Http请求,返回请求响应 * * @param url url地址 * @return */ private HttpResponse send(String url) throws Exception { try { // 初始化Http请求方法和参数 initGet(url); // 进行Http请求 return doRequest(); } catch (Exception e) { e.printStackTrace(); throw new Exception("http send request exception"); } } /** * Get方法 * * @param url */ private void initGet(String url) { request = new HttpGet(url); } /** * 初始化HttpClient对象 * * @return HttpClient对象 */ private HttpClient getHttpClient() { HttpClient httpClient = null; try { //ThreadSafeClientConnManager\PoolingClientConnectionManager httpClient = new DefaultHttpClient(new PoolingClientConnectionManager(getSchemeRegistry()), prepareHttpParams()); } catch (Exception e) { e.printStackTrace(); httpClient = new DefaultHttpClient(); } return httpClient; } /** * 进行Http请求,得到Http响应 * * @return Http响应 * @throws Exception */ private HttpResponse doRequest() throws Exception { HttpClient httpClient = getHttpClient(); // setProxy(httpClient);// 设置代理 return httpClient.execute(request); } }
从上面代码可以看到,HttpHelper对象是DataServiceImpl 的全局变量,虽然HttpHelperHttpClient是通过PoolingClientConnectionManager 获取HttpClient的,没有进行同步操作,在多线程就很可能会出现获取出来的对象是相同的,(个人理解),这里抛砖引玉,哪位牛人帮详解下,^_^。
那接下来怎么修改呢,很简单,只修改HttpHelper中的HttpClient变量使用范围:
public class HttpHelper { /** HttpClient对象 */ private HttpClient httpClient; /** * 构造函数 */ public HttpHelper() { httpClient = getHttpClient(); } /** * 发送Http请求,返回文本形式的请求响应 * * @param url url地址 * @return */ public String requestString(String url) { HttpGet request = null; try { // 初始化Http请求方法和参数 request = new HttpGet(url); // 进行Http请求 HttpResponse stringResponse = httpClient.execute(request); // 判断请求是否成功 if (!isResponseSuccessStatus(stringResponse)) { return null; } System.out.println(Thread.currentThread().getName() + "---url:" + url); return parseHttpResponse2String(stringResponse); } catch (Exception e) { request.abort(); httpClient.getConnectionManager().shutdown(); e.printStackTrace(); System.out.println("requestString()" + url); return null; } } /** * 初始化HttpClient对象 * * @return HttpClient对象 */ private HttpClient getHttpClient() { try { // ThreadSafeClientConnManager\PoolingClientConnectionManager PoolingClientConnectionManager pccm = new PoolingClientConnectionManager(getSchemeRegistry()); // pccm.setDefaultMaxPerRoute(20); httpClient = new DefaultHttpClient(pccm, prepareHttpParams()); } catch (Exception e) { e.printStackTrace(); httpClient = new DefaultHttpClient(); } return httpClient; } }
有几点自己以后要注意:
1、要注意代码细节,多测试才能发现问题。
2、程序引用的代码即使不能钻研的很明白,至少能理解使用的具体场合。
3、最重要的一点,以后要恶补基础啦, 三天不学习,不知道南北极哪,