3

長いコード投稿で申し訳ありませんが、誰かがマルチスレッドの質問を手伝ってくれるかどうか疑問に思っています (私はマルチスレッドにまったく慣れていません)。複数のスレッドで共有できる RESTFUL Web サービス API にファサード クラスを設計しようとしています。私は HttpURLConnection を使用して接続を行い、Google GSON を使用して JSON データとの間で変換しています。

以下のクラスは、私がこれまでに持っているものです。この例では、API 呼び出し (authenticateCustomer()) を行うためのパブリック メソッドが 1 つあり、API 呼び出しを容易にするためにプライベート メソッドが使用されます (つまり、POST データ文字列の作成、POST リクエストの作成など)。

このクラスのインスタンスを 1 つ作成し、それを 1000 のスレッドと共有します。スレッドは、authenticateCustomer() メソッドを呼び出します。ほとんどのスレッドは機能しますが、同期を実装していないため、null ポインター例外が発生するスレッドがいくつかあります。authenticateCustomer() メソッドを「同期」にすると、機能します。問題は、これにより同時実行性が低下することです (たとえば、POST 要求が完了するまでに突然長い時間がかかり、他のすべてのスレッドが停止するなど)。

今私の質問に。以下のクラスはステートレスではないため、スレッドセーフですか? クラス内にあるごく少数のフィールドが final と宣言され、コンストラクターで割り当てられます。すべてのメソッドはローカル変数を使用します。Gson オブジェクトは (Web サイトによると) ステートレスであり、とにかく API メソッドでローカル変数として作成されます。

public final class QuizSyncAPIFacade 
{
    // API Connection Details
private final String m_apiDomain;
private final String m_apiContentType;
private final int m_bufferSize;

// Constructors
public QuizSyncAPIFacade()
{
    m_apiDomain      = "http://*****************************";
    m_apiContentType = ".json";
    m_bufferSize = 8192; // 8k
}

private String readInputStream(InputStream stream) throws IOException
{
        // Create a buffer for the input stream
    byte[] buffer = new byte[m_bufferSize];

    int readCount;

    StringBuilder builder = new StringBuilder();

    while ((readCount = stream.read(buffer)) > -1) {
        builder.append(new String(buffer, 0, readCount));
    }

    return builder.toString();
}

private String buildPostData(HashMap<String,String> postData) throws UnsupportedEncodingException
{
    String data = "";

    for (Map.Entry<String,String> entry : postData.entrySet()) 
    {
        data += (URLEncoder.encode(entry.getKey(), "UTF-8") + "=" + URLEncoder.encode(entry.getValue(), "UTF-8") + "&");        
    }

    // Trim the last character (a trailing ampersand)
    int length = data.length();

    if (length > 0) {
        data = data.substring(0, (length - 1));
    }

    return data;
}

private String buildJSONError(String message, String name, String at)
{
    String error = "{\"errors\":[{\"message\":\"" + message + "\",\"name\":\"" + name + "\",\"at\":\"" + at + "\"}]}";

    return error;
}

private String callPost(String url, HashMap<String,String> postData) throws IOException
{
    // Set up the URL for the API call 
    URL apiUrl = new URL(url);

    // Build the post data
    String data = buildPostData(postData);

    // Call the API action
    HttpURLConnection conn;

    try {
        conn = (HttpURLConnection)apiUrl.openConnection();
    } catch (IOException e) {
        throw new IOException(buildJSONError("Failed to open a connection.", "CONNECTION_FAILURE", ""));
    }

    // Set connection parameters for posting data
    conn.setRequestMethod("POST");
    conn.setUseCaches(false);
    conn.setDoInput(true);
    conn.setDoOutput(true);

    // Write post data
    try {
        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
        wr.writeBytes(data);
        wr.flush();
        wr.close();
    } catch (IOException e) {
        throw new IOException(buildJSONError("Failed to post data in output stream (Connection OK?).", "POST_DATA_FAILURE", ""));           
    }

    // Read the response from the server                
    InputStream is;

    try {
        is = conn.getInputStream();
    } catch (IOException e) {
        InputStream errStr = conn.getErrorStream();

        if (errStr != null) 
        {
            String errResponse = readInputStream(errStr);
            throw new IOException(errResponse);
        } 
        else 
        {
            throw new IOException(buildJSONError("Failed to read error stream (Connection OK?).", "ERROR_STREAM_FAILURE", ""));
        }
    }

    // Read and return response from the server
    return readInputStream(is);
}

/* -------------------------------------
 * 
 * Synchronous API calls
 * 
   ------------------------------------- */

public APIResponse<CustomerAuthentication> authenticateCustomer(HashMap<String,String> postData)
{
    // Set the URL for this API call
    String apiURL = m_apiDomain + "/customer/authenticate" + m_apiContentType;

    Gson jsonConv = new Gson();

    String apiResponse = "";

    try 
    { 
        // Call the API action
        apiResponse = callPost(apiURL, postData);

        // Convert JSON response to the required object type
        CustomerAuthentication customerAuth = jsonConv.fromJson(apiResponse, CustomerAuthentication.class);

        // Build and return the API response object
        APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(true, customerAuth, null);

        return result;
    } 
    catch (IOException e) 
    {
        // Build and return the API response object for a failure with error list
        APIErrorList errorList = jsonConv.fromJson(e.getMessage(), APIErrorList.class);

        APIResponse<CustomerAuthentication> result = new APIResponse<CustomerAuthentication>(false, null, errorList);

        return result;
    }
}

}

4

2 に答える 2

2

エラーが発生する場合は、認証サービスを過負荷にしている可能性があります (一度に 1 つずつ実行すると発生しないことです)。おそらく、500、503、または 504 などのエラーが返されますが、無視して取得することができます。何も期待しない場合は、 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htmlを返します。null

1000 cpu を持っていないと仮定すると、より少ないスレッドを使用します。この数のスレッドを使用すると、より効率的ではなく遅くなる可能性があります。

また、サービスが毎回正しく返されていることを確認し、null値が得られる理由を調査します。

サービスが一度に 20 個のリクエストしか処理できない場合はSemaphore、最後の手段として を試すことができます。これは、同時リクエストの数を制限するために使用できます。

于 2012-05-04T11:37:33.177 に答える
0

ステートレス クラスは、アクセスするオブジェクトがスレッドに対してプライベートであるか、それ自体がスレッド セーフである場合、本質的にスレッド セーフです。

于 2012-05-04T11:39:56.293 に答える