2014-01-26 2 views
0

내 Java 클라이언트의 동기식 및 비동기식 메서드가 필요한 프로젝트에서 작업 중입니다. 일부 고객은 동기식을 호출하고 일부 고객은 거기에 따라 Java 클라이언트의 비동기식 메소드를 호출합니다. 호출 인터페이스를 구현하는 내 ClientTask 클래스이고 나는 ClientTask class에서 DI 패턴을 사용하여 종속성 주위를 통과하고 지금 아래멀티 스레딩 환경에서 빌더 패턴 스레드를 안전하게 만드는 방법은 무엇입니까?

public class TestingClient implements IClient { 

    private ExecutorService service = Executors.newFixedThreadPool(10); 
    private RestTemplate restTemplate = new RestTemplate(); 

    // for synchronous 
    @Override 
    public String executeSync(ClientKey keys) { 

     String response = null; 
     try { 

      Future<String> handle = executeAsync(keys); 
      response = handle.get(keys.getTimeout(), TimeUnit.MILLISECONDS); 
     } catch (TimeoutException e) { 

     } catch (Exception e) { 

     } 

     return response; 
    } 

    // for asynchronous 
    @Override 
    public Future<String> executeAsync(ClientKey keys) { 

     Future<String> future = null; 

     try { 
      ClientTask ClientTask = new ClientTask(keys, restTemplate); 
      future = service.submit(ClientTask); 
     } catch (Exception ex) { 

     } 

     return future; 
    } 
} 

그리고 -

다음은 synchronousasynchronous 방법을 가지고 내 자바 클라이언트입니다. 통화 방법, 난 그냥 machineIPAddress에 URL 기반을 만들고 ClientTask 클래스에 전달되는 ClientKeys를 사용하여 다음 RestTemplate를 사용하여 서버를 공격하고 다시 응답을하고있다 -

class ClientTask implements Callable<String> { 

    private ClientKey cKeys; 
    private RestTemplate restTemplate; 

    public ClientTask(ClientKey cKeys, RestTemplate restTemplate) { 
     this.restTemplate = restTemplate; 
     this.cKeys = cKeys; 
    } 

    @Override 
    public String call() throws Exception { 

     // .. some code here 
     String url = generateURL("machineIPAddress");   
     String response = restTemplate.getForObject(url, String.class); 

     return response; 
    } 

    // is this method thread safe and the way I am using `cKeys` variable here is also thread safe? 
    private String generateURL(final String hostIPAdress) throws Exception { 
     StringBuffer url = new StringBuffer(); 
     url.append("http://" + hostIPAdress + ":8087/user?user_id=" + cKeys.getUserId() + "&client_id=" 
      + cKeys.getClientId()); 

     final Map<String, String> paramMap = cKeys.getParameterMap(); 
     Set<Entry<String, String>> params = paramMap.entrySet(); 
     for (Entry<String, String> e : params) { 
      url.append("&" + e.getKey()); 
      url.append("=" + e.getValue()); 
     } 

     return url.toString(); 
    } 
} 

을 그리고 아래에있는 내 ClientKey 클래스 고객이 입력 매개 변수를 만들기 위해 사용 빌더 후두둑를 사용하면 TestingClient에 전달 - 나는 multithrea에서 ClientTask 클래스 ClientKey 변수를 사용하고 같은

public final class ClientKey { 

    private final long userId; 
    private final int clientId; 
    private final long timeout; 
    private final boolean testFlag; 
    private final Map<String, String> parameterMap; 

    private ClientKey(Builder builder) { 
    this.userId = builder.userId; 
    this.clientId = builder.clientId; 
    this.remoteFlag = builder.remoteFlag; 
    this.testFlag = builder.testFlag; 
    this.parameterMap = builder.parameterMap; 
    this.timeout = builder.timeout; 
    } 

    public static class Builder { 
    protected final long userId; 
    protected final int clientId; 
    protected long timeout = 200L; 
    protected boolean remoteFlag = false; 
    protected boolean testFlag = true; 
    protected Map<String, String> parameterMap; 

    public Builder(long userId, int clientId) { 
     this.userId = userId; 
     this.clientId = clientId; 
    } 

    public Builder parameterMap(Map<String, String> parameterMap) { 
     this.parameterMap = parameterMap; 
     return this; 
    } 

    public Builder remoteFlag(boolean remoteFlag) { 
     this.remoteFlag = remoteFlag; 
     return this; 
    } 

    public Builder testFlag(boolean testFlag) { 
     this.testFlag = testFlag; 
     return this; 
    } 

    public Builder addTimeout(long timeout) { 
     this.timeout = timeout; 
     return this; 
    } 

    public ClientKey build() { 
     return new ClientKey(this); 
    } 
    } 

    public long getUserId() { 
    return userId; 
    } 

    public int getClientId() { 
    return clientId; 
    } 

    public long getTimeout() { 
    return timeout; 
    } 

    public Map<String, String> getParameterMap() { 
    return parameterMap; 

    public boolean istestFlag() { 
    return testFlag; 
    } 
} 

나의 위의 코드 스레드 안전한가요 다른 스레드가 TestingClient 동기 메서드를 호출하는 동안 ClientKey 변수를 변경하려고하면 어떤 일이 발생할지 확실하지 않음 -

고객이 아래 코드를 사용하여 전화를 걸고 우리를 호출 할 수 있습니다. 뿐만 아니라이 멀티 스레드 응용 프로그램 -

IClient testClient = ClientFactory.getInstance(); 

Map<String, String> testMap = new LinkedHashMap<String, String>(); 
testMap.put("hello", "world"); 

ClientKey keys = new ClientKey.Builder(12345L, 200).addTimeout(2000L).parameterMap(testMap).build(); 

String response = testClient.executeSync(keys); 

그래서 단지 그들이 여러 스레드에서 내 TestingClient 클래스에 여러 값을 전달할 수있는 내 위의 코드가 안전 여부를 스레드할지 여부를 이해하려고 노력. 때문에 내 ClientKey 클래스가 스레드로부터 안전하지 않지만 확실하지 않은 느낌이 들었습니다.

여기에 StringBuffer이 필요하거나 StringBuilder은 괜찮습니다. 동기화되지 않았기 때문에 StringBuilder가 StringBuffer보다 빠르기 때문에 괜찮습니다.

아무도 도와 줄 수 있습니까?

+0

ClientKey에 할당 된 후에 parameterMap을 수정할 수 없도록하려면, 다음과 같이해야합니다 :'this.parameterMap = Collections.unmodifiableMap (new HashMap (builder.parameterMap)) '하지만 여러 스레드가 현재 동기화되지 않은 LinkedHashMap에 액세스하는 경우 이미 ClientKey 외부의 코드에 문제가 있습니다. –

+0

Erwin 제안에 감사드립니다. 고객이 다른 스레드에서 'TestingClient' 클래스로 다른 값을 전달할 수 있습니다. 즉, ClientId 클래스를 사용하여 userId가 전달하는 각 호출에 대해 계속해서 입력 매개 변수를 작성해야합니다. ParameterMap에서도 마찬가지입니다. 그리고 LinkedHashMap 코드는 고객이 애플리케이션에서 우리에게 어떻게 전화를 걸지를 보여주는 예입니다. 그들은 그 곳에서 ConcurrentHashMap을 가질 수 있습니다. 우리에게 전화하는 방법을 코드화합니다 .. –

답변

1

매개 변수 ClientKey keys이 주어 지므로 항상 다른 것으로 가정합니다.

난 당신의 코드에 어떤 동기화 문제가 표시되지 않습니다, 나는 설명 할 것이다 :

ClientTask ClientTask = new ClientTask(keys, restTemplate); 
future = service.submit(ClientTask); 
  • 스레드간에 공유되지 않는 방법, 내부에서 ClientTask 객체를 생성. 내가 전에 말했듯이 같이 ClientKeys 객체가 매개 변수로 주어진, 그래서 당신이만큼 좋은됩니다,
  • ClientTask 객체는 방법 generateURL 내부의 키를 읽기 미래의 객체를 반환하지만 whih service.submit를 사용
  • , 개체가 공유되지 않습니다.

요약하면 코드의 스레드 안전성은 ExecutorServiceFuture이 스레드로부터 안전합니다.

업데이트 : 명확한 설명 as long as this object is not being shared

ClientKeys keys; 
add keys to @keys 
.. code 
executeAsync(.., keys) 
... code 
add keys to @keys 
add keys to @keys 
executeAsync(.., keys) 
executeAsync(.., keys) 
add keys to @keys 
... code 
add keys to @keys 
executeAsync(.., keys) 

에 대한이 내가 공유하고 무엇을 의미하는지 (더 이하)이다. keys이 executeAsync() 호출로 인해 여러 스레드에서 사용되고 있습니다. 이 경우 일부 스레드는 keys을 읽고 다른 스레드는 writing 데이터를 가지고 있는데, 이는 실제로는 race condition이라고합니다.

업데이트 2 : StringBuffer 개체는 로컬 일 수 있으며 (의 범위에 속함) 동기화 할 필요가 없습니다.

+1

+1 답장 : P – Muel

+0

제안에 감사하는 ichramm입니다. 나는이 물체가 공유되지 않는 한 사람들이 말할 때 항상 혼란스러워합니다. 이것이 실제로 의미하는 바는 무엇입니까? Clientkey 키는 각 통화마다 다를 수 있습니다. –

+0

내 시나리오에서 이것이 일어날 것 같습니까? –

관련 문제