2016-06-09 2 views
4

ConcurrentHashMap을 사용할 때 일부 동기화 블록을 코드에 추가해야 할 때를 알아야합니다. 다음과 같은 방법이 있다고 가정 해 보겠습니다.ConcurrentHashMap의 추가 동기화는 언제 어떻게 사용해야합니까?

private static final ConcurrentMap<String, MyObjectWrapper> myObjectsCache = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY); 

    public List<MyObject> aMethod(List<String> ids, boolean b) { 
     List<MyObject> result = new ArrayList<>(ids.size()); 
     for (String id : ids) { 
      if (id == null) { 
       continue; 
      } 
      MyObjectWrapper myObjectWrapper = myObjectsCache.get(id); 
      if (myObjectWrapper == null) { 
       continue; 
      } 
      if (myObjectWrapper.getObject() instanceof MyObjectSub) { 
       ((MyObjectSub) myObjectWrapper.getObject()).clearAField(); 
       myObjectWrapper.getObject().setTime(System.currentTimeMillis()); 
      } 
      result.add(myObjectWrapper.getObject()); 
      if (b) { 
       final MyObject obj = new MyObject(myObjectWrapper.getObject()); 
       addObjectToDb(obj); 
      } 
     } 
     return result; 
    } 

효율적으로이 방법을 어떻게 만들어야합니까? "get"은 안전하지만 일단 캐시에서 값을 가져 와서 캐시 된 객체의 필드를 업데이트하면 다른 스레드가 동일한 래퍼를 가져 와서 동일한 기본 객체를 업데이트하려고 할 때 문제가 발생할 수 있습니다. 동기화를 추가 하시겠습니까? 그렇다면 "get"에서 루프 반복 또는 전체 루프의 끝까지 동기화해야합니까? 좀 더 작업 등 루프 내부의지도 키/값을 수행해야 할 때

아마 누군가가

정말 감사하겠습니다 ... ConcurrentHashMap의의 적절하고 효율적으로 사용의 좀 더 구체적인 가이드 라인을 공유 할 수 있습니다.

편집 : 질문에 대한 몇 가지 상황 : 나는 현재 생산 코드의 일부 DAO 클래스의 리팩토링에 일하고 있어요 및 데이터베이스에서 검색 데이터를 캐싱 HashMaps을 사용하는 클래스의 몇 가지. 캐시를 사용하는 모든 메소드 (쓰기 또는 읽기 용)는 동기화 된 (캐시) 블록 내에 전체 내용을 담고있었습니다 (안전한 것으로?). 동시성에 대한 많은 경험이 없기 때문에이 기회를 통해 배우고 싶습니다. 순진하게 HashMaps를 ConcurrentHashMaps로 변경했으며, 이제는 필요한 곳에 동기화 된 블럭을 제거하려고합니다. 모든 캐시는 쓰기 및 읽기에 사용됩니다. 제시된 방법은 내가 변경 한 방법 중 하나를 기반으로하고 있으며 지금은 언제 그리고 어느 정도 동기화를 배우려고합니다. 메소드 clearAField는 랩핑 된 POJO 오브젝트의 필드 중 하나의 값을 변경하고 addObjectToDb는 오브젝트를 데이터베이스에 추가하려고 시도합니다.

다른 예는 캐시의 보충 될 것이다 :

public void findAll() throws SQLException{ 
    // get data from database into a list 
    List<Data> data=getAllDataFromDatabase(); 
    cacheCHM.clear(); 
    cacheCHM.putAll(data); 
} 

하는 경우 I는 동기화 (cacheCHM) 블록 내부의 명확하고 putAll에 넣어해야, 오른쪽?

내가 찾을 일부 게시물을 읽으려고했습니다/기타 루프가없는 단일 작업과 CHM의 적절하고 효율적으로 사용할 수 있지만, 대부분의 거래에 대한 기사는 .... 내가 찾은 가장은 다음과 같습니다 http://www.javamadesoeasy.com/2015/04/concurrenthashmap-in-java.html

+0

꽤 많이 당신이 응용 프로그램 논리에 따라 달라집니다. – pintxo

+2

검색 값을 스레드로부터 안전하도록 업데이트해야하는 경우 개체 자체에서 동기화해야합니다. 'ConcurrentHashMap'은 포함 된 값이 아닌 맵 자체의 구조 (즉, 키와 값의 관계)만을 보호합니다. –

+0

@Jim에 동의합니다. 동시 맵은 구조 만 보호합니다 (예 : 키와 값의 관계). 위의 코드 컨텍스트에 따라 하나만 더 추가하고 싶습니다. 즉, ie.e myObjectsCache.get (id) 값을 읽는 것입니다.이 목적을 위해 호출 할 때까지 동시 맵이 필요하지 않을 수도 있습니다. map.put(). – pbajpai21

답변

1

앱에서 어떤 동시성이 발생할 것으로 예상하는지에 대해서는 언급하지 않았으므로, aMethod을 호출하는 다중 스레드가 있다고 가정합니다..

ConcurrentHashMap에 대한 단일 호출 만 있습니다 : myObjectsCache.get(id), 괜찮습니다. 실제로 아무 것도 당신의 objectCache에 데이터를 쓰지 않기 때문에 [위의 가정을 보아라] 당신은 ConcurrentHashMap이 필요조차 없다! 불변의 콜렉션은 괜찮을 것입니다. 마지막에 의심스러운 행이 있습니다 : addObjectToDb(obj),이 방법이 캐시에도 영향을 줍니까? 그렇다면 여전히 안전합니다 (확실한 방법을 찾아야합니다).하지만 ConcurentHashMap이 꼭 필요합니다. 당신이 개체를 변경할 경우

위험

여기입니다 :

myObjectWrapper.getObject().clearAField(); 
myObjectWrapper.getObject().setTime(System.currentTimeMillis()); 

여러 스레드가 동시에 같은 객체에서 이러한 메서드를 호출하는 것이 가능합니다. 이 방법이 무엇을하는지 모른 채, 이것이 안전한지 여부는 말할 수 없습니다.이러한 메소드가 모두 동기화 된 것으로 표시되거나 이러한 메소드가 동시에 실행되는 것이 안전한지 확인하기 위해주의를 기울인 경우에는 괜찮습니다 (그러나 이러한 메소드가 다른 순서로 실행되어 직관적으로 기대할 수있는 범위가 있음을 유의하십시오!). 그렇게 신중하지 않으면 데이터가 손상 될 가능성이 있습니다.

+0

매우 구체적이지 않아서 죄송합니다. SQL 데이터베이스에서 검색된 데이터를 저장하는 DAO 클래스 중 일부에 캐시 맵이 있습니다. 이러한 캐시는 캐시 된 오브젝트에 대한 일부 조작을 수행 할 수 있습니다. 이 클래스는 캐시에 HashMaps를 사용하고 캐시를 사용하는 메소드의 모든 내용을 맵에서 잠금과 동기화했습니다. 나는 코드를 리팩토링하여보다 효율적이고 캐시 응집력을 유지하고자한다. 예제에서 clearAField와 setTime 메서드는 동기화되지 않았습니다. 이들은 pojo의 일반 설정자입니다. – ssr

+0

질문의 범위가 매우 넓게 들리기 시작했습니다. 간단히 말해서 세계를 스레드 세이프로 만드는 가장 간단한 방법은 동시 안전 컨테이너 안에 불변 객체를 사용하는 것입니다. 여기서하는 것처럼 컨테이너 내부의 오브젝트를 변형 시키려고 할 때 (복제하지 않고), 잠재적 인 문제에 노출됩니다. – Matthew

+0

그래서 캐시가 아닌 모든 비동기 작업을 동기화해야합니다. 캐시 된 객체를 retreiving 한 후에 캐시 된 객체를 변경하는 경우 (제거 된 객체 수가 너무 많습니까? 아니면 "get"만 사용합니까?) - 동기화 블록에 해당 코드를 포함해야합니다. 캐시 된 객체를 변경할 수 없다고 생각합니까?) 보시다시피, 나는 손으로 나를 이끌어 줄 누군가를 찾고 있는데 ... 나는 나의 변화가 유효 할 것이 틀림 없기 때문에. – ssr

0

threadsaftey와 cache에 대한 더 나은 접근법은 immutable objects을 사용하는 것입니다. 여기에 MyObjectSub의 CALSS이 불변이라면 어떻게 보이는지의 [랩퍼가 필요한 이유를 확실하지 - 그게 완전하게 가능하다 생략 것] :

//Provided by way of example. You should consider generating these 
//using http://immutables.github.io/ or similar 
public class MyImmutableObject { 
    //If all members are final primitives or immutable objects 
    //then this class is threadsafe. 
    final String field; 
    final long time; 

    public MyImmutableObject(String field, long time) { 
     this.field = field; 
     this.time = time; 
    } 

    public MyImmutableObject clearField() { 
     //Since final fields can never be changed, our only option is to 
     //return a copy. 
     return new MyImmutableObject("", this.time); 
    } 

    public MyImmutableObject setTime(long newtime) { 
     return new MyImmutableObject(this.field, newtime); 
    } 
} 

다음의 안전을 불변으로 쓰레드의 오브젝트가 많은 간단한 경우 . 귀하의 방법은 다음과 같이 보일 것입니다 :

public List<Result> typicialCacheUsage(String key) { 
    MyImmutableObject obj = myObjectsCache.get(key); 

    obj = obj.clearField(); 
    obj = obj.setTime(System.currentTimeMillis()); 

    //If you need to put the object back in the cache you can do this: 
    myObjectsCache.put(key, obj); 

    List<Result> res = generateResultFromObject(obj); 
    return res; 
} 
+0

별도의 게시판으로 게시하는 대신 기존 답변에 추가하면 더 좋을 것입니다. 이는 질문에 대한 답변이 아닙니다. – Hulk

관련 문제