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

博客探讨了HttpClient在多线程环境下的线程安全问题。首先,展示了线程安全的HttpClient封装,然后通过测试揭示了单例模式下HttpClient的线程不安全性,并通过MultiThreadedHttpConnectionManager提供了解决方案,确保了多线程环境下HttpClient的正确使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于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(); 
    }

}

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

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值