長いコードポストをお詫びしますが、誰かがマルチスレッドに関する質問をすることができるのだろうかと疑問に思っています。私は、複数のスレッドと共有できる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;
}
}
}
NPEはどこで入手できますか? –
このクラスは確実にスレッドセーフです – kromit
渡されたHashMapを直接使用します(コピーなし)。使用中に別のスレッドによって変更された場合は、記述した問題が発生する可能性があります。最初にローカル変数にコピーし、ローカル変数の内容が有効であることを確認してから、ローカル変数を使用します。 – assylias