2012-05-04 17 views
3

長いコードポストをお詫びしますが、誰かがマルチスレッドに関する質問をすることができるのだろうかと疑問に思っています。私は、複数のスレッドと共有できるRESTFUL WebサービスAPIにファサードクラスを設計しようとしています。私は接続を行うためにHttpURLConnectionを使用しており、Google GSONはJSONデータとの変換を行います。マルチスレッド(ステートレスクラス)

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

私はこのクラスのインスタンスを1つ作成し、それを1000スレッドで共有します。スレッドはauthenticateCustomer()メソッドを呼び出します。スレッドのほとんどは動作しますが、私は同期を実装していないため、NULLポインタ例外を取得するスレッドがあります。私がauthenticateCustomer()メソッドを '同期'させると、それは機能します。問題は、このことが並行性が悪くなることです(たとえば、POST要求が突然完了するまでに時間がかかり、他のすべてのスレッドを保持するなど)。

今質問します。以下のクラスはステートレスなのでスレッドセーフではありませんか?クラス内の非常に少数のフィールドはfinal宣言され、コンストラクターで割り当てられます。すべてのメソッドはローカル変数を使用します。 Gsonオブジェクトはステートレスで(ウェブサイトによると)、とにかく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; 
    } 
} 

}

+3

NPEはどこで入手できますか? –

+1

このクラスは確実にスレッドセーフです – kromit

+2

渡されたHashMapを直接使用します(コピーなし)。使用中に別のスレッドによって変更された場合は、記述した問題が発生する可能性があります。最初にローカル変数にコピーし、ローカル変数の内容が有効であることを確認してから、ローカル変数を使用します。 – assylias

答えて

2

あなたがエラーを取得している場合は、認証サービス(一度にこれを行う場合は発生しません、何かを)オーバーロードしているので、それはおそらく、それはのようなエラーを返すことができ500、503または504あなたは無視して、あなたが戻って期待する何を得ることができなかった、あなたはnullhttp://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

を返す私はあなたが1000個のCPU、この多くのスレッドが遅くなります持つのではなく、その可能性を持っていないと仮定すると以下のスレッドを使用しますより多くのefficeint。

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

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

+3

CPUコアよりもはるかに多くのスレッドを使用することは、I/Oをほとんど待たなければならないような場合には、実際には意味があります。もちろん、非ブロッキングI/Oに基づくアプローチを検討する価値はありますが、 – x4u

+1

これは、サービスまたはIOのスループットがより多くの同時要求を持つことによって改善されるならば、正しいです。並行性によってはスループットが向上する場合がありますが、場合によっては最適値がかなり小さくなることもあります。明らかに、サービスが処理できないより多くの要求を行うと、失敗につながる可能性があります。 –

+1

それはピーターだった、ありがとう!私は私の認証サービスを過負荷にしていました。私が作成したスレッドが多いほど、その割合は大きくなり、誤って戻ってきていました。 NPEは、スレッド安全性の問題ではなく、ワーカースレッドでサービスからの応答を適切に検証しないために発生しました。検証を修正したら、特定の数のワーカースレッドを上回ると、500回のエラーが発生していました。 – RWilson

0

すべてのステートレスクラスは、スレッドがプライベートであるかスレッドセーフであるかにかかわらず、本質的にスレッドセーフです。