项目开发中碰到的一个线程有关问题 (二)

项目开发中碰到的一个线程问题 (二)
         前几天碰到了一个问题项目开发中碰到的一个线程问题(一),当时说的不太清楚项目开发中碰到的一个线程有关问题 (二) ,这里好好总结下。
         现象:多线程Http请求,在服务端发现总会有相同参数的请求。当时认为是HttpClient在多线程下是线程非安全的,wangzhangxing 提到:
引用
PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
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、最重要的一点,以后要恶补基础啦, 三天不学习,不知道南北极哪,项目开发中碰到的一个线程有关问题 (二)