2011-01-07 1 views
3

나는 제어 할 수없는 두 가지 동기화 된 메서드가있는 클래스를 사용하는 병렬로 많은 스레드가 액세스하는 메서드가 있습니다. getObject 및 createNewObject. 나는 여러 객체 (MyObject)를 생성하지 않도록하고 싶다. 스레드가 너무 다른 스레드에서 올 수뿐만 아니라 객체를 생성 얻고 만드는 방법 사이 일시 중지 될 수 있기던져 예외 vs 동기화

MyObject obj; 
public void method1() { 
    obj = getObject("key"); 
    if (obj == null) 
     obj = createNewObject("key"); 
    } 

이, 내가 생각 작동하지 않을 것입니다. 동기 된 createNewObject 메쏘드는 객체가 "key"라는 이름으로 이미 존재하는지 검사하고, 그 경우에 예외를 throw함으로써 이것을 수정합니다.

다음 중 어떤 방법을 선호합니까? 성능, 안전성 및 디자인이 현명합니다. 이중 잠금 유형 (방법 3)이 작동하지 않는다고 들었습니다. 어쩌면 나는 방법 1을 사용해야 만 할까?

대부분의 경우 개체가 발견되어 아무런 문제가 없습니다. 드물게 동기화를 건너 뛰고 예외를 처리하는 것이 더 나은 성능 일 수 있습니까?

MyObject obj; 
public synchronized void method1() { 
    obj = getObject("key"); 
    if (obj == null) 
     obj = createNewObject("key"); 
    } 

public void method2() { 
    obj = getObject("key"); 
    if (obj == null) 
     try { 
      obj = createNewObject("key"); 
     } catch (Exception e) { // ops, someone already created object "key" 
      obj = getObject(); 
     } 
    } 

public void method3() { 
    obj = getObject("key"); 
    if (obj == null) 
     obj = getObj("key"); 
} 
public synchronized MyObject getObj(String key) { 
    MyObject obj = getObject(key); 
    if (obj == null) 
     obj = createNewObject(key); 
    return obj; 
} 
+2

method3이 작동하지 않는 이유에 대한 자세한 내용은 http://www.ibm.com/developerworks/java/library/j-dcl.html을 확인하십시오. – gabuzo

답변

4

약간의 테스트와 프로파일 링이 필요하지만 모든 경우에 getObject() 메소드를 호출 할 때 어떤 경우에도 동기화가 수행되기 때문에 중요한 성능을 얻지 못할 것이라는 확신이 있습니다. 동기화됩니다. 그래서 이것은 "동기화/동기화 없음"종류의 차이가 아니라 "동기화/이중 동기화"입니다. 그렇게 많이는 안됩니다. 어쨌든 동기화하고 있다면 최대한으로 수행하는 것이 좋습니다. 귀하의 경우에는 method1()을 의미합니다.

UPDATE 방법 2()가 너무 유망 보일 수 있지만, 난 그냥에 문제가 실현

: 그것은 obj 필드에 쓰기를 동기화하지 않기 때문에, 다른 스레드가 업데이트 된 값을 볼 수 없습니다를 . 따라서 obj 필드가 method2()를 호출하는 스레드가 아닌 다른 스레드에 의해 액세스되는 경우 method2()가 올바르지 않습니다.

obj 필드를 휘발성으로 만들면 getObject()가 동기화되어 "비 휘발성 개체에 대한 휘발성 참조"문제가 없어야하므로 작동하지 않을 수 있습니다 (100 % 확신 할 수 없음). getObject()가 반환 된 후에는 쓰기 장벽을 수행하므로 완전히 초기화 된 객체가 주 메모리에 있음을 보장합니다. 그리고 어떤 스레드도 그 객체의 로컬 캐시 된 사본을 가지고 있지 않기 때문에 어떤 스레드가 obj 필드에 접근해도 괜찮습니다. obj 필드에 의해 참조되는 객체가 변경 가능하지 않은 경우,이 경우 모든 액세스가 동기화되어야합니다.

그래도 여전히 의미가 없습니다. 완전히 비 동기화 된 읽기 액세스는 여전히 불가능하기 때문에 깨끗한 구현은 여전히 ​​"똑똑한"구현보다 낫습니다.

+0

RE : Method2. 그러나 createNewObject 메쏘드는 동기화되어 있습니다. 이미 존재하는 키를 가진 객체를 생성하려고한다면, 예외가 발생합니다. 그래서 일어날 수있는 유일한 일은 다른 스레드가 obj 필드가 누군가가 쓰기 전에 null이라는 것을 보는 것입니다. 따라서 스레드는 개체 자체를 만들려고 시도하지만 다른 스레드가 방금 개체를 만들었 기 때문에 예외가 발생합니다. – KTrum

+0

@Karl,'obj' 필드가 휘발성이 아니라면 누군가 다른 스레드가 쓰기 후에도 다른 스레드가이를 null로 볼 수 있습니다. –

+0

@Sergey, 예, 이해합니다. 그러나 그 결과는 다른 스레드가 새 객체를 생성하려고 시도하고 (동기화 됨) 예외가 발생한다는 것입니다. createNewObject를 호출하면 객체가 이미 생성 된 것을 알 수 있기 때문입니다. createNewObject에는 생성 된 객체를 추적하는 내부 해시 맵이 있으며이 해시 맵에 대한 모든 액세스는 동기화됩니다. – KTrum

3

현대 VM의 동기화는 리소스/실행 시간을 거의 소비하지 않습니다. 나는 단순히 check/create 메소드를 동기화 할 것이다. 조숙 한 최적화는 많은 시간을 필요로 할 것이고 문제가되는 경우 이런 종류의 문제에 대해 걱정할 필요가 없습니다.

8

프로필러가 병목이라고 알릴 때까지 method1부터 시작하십시오. 그것은 가장 깨끗한 구현이며 항상 올바르게 작동한다는 것을 알고 있습니다. 나중에 연속 통화로 많은 시간을 낭비한다는 데이터가 표시되면 다른 시도를 생각할 수 있습니다.

+0

그리고 이미 소유 한 뮤텍스를 재 획득하는 것은 현대 jvms에서 빠르다. 그래서 method1은 분명히 선호되며, 다르게 그리고 원래대로 수행하지 않을 것이다. – jtahlborn

+0

'조기 최적화는 모든 악의 근원입니다' – Qix

0

편집 : 나는 두 번 ideom 잠금 확인 썼다, 그러나

---- 원래 대답에 설명 된 몇 가지 중요한 결함을 가지고 아래의 아래 ----

가장 좋은 해결책은 다음과 같습니다.

Object obj; 

public Object getObject() { 
    if(obj == null) { 
     synchronized(this) { // or on something else 
      if(obj == null) { 
       obj = createObject(); 
      } 
     } 
    } 
    return obj; 
} 

private Object createObject() { 
    ... 
} 

동기화는 개체의 중요한 생성 단계에서만 발생하지만 여전히 작동한다는 장점이 있습니다.

+0

이 내 방법 3과 실제로 같지 않습니까? 이 이중 잠금 메커니즘이 어떻게 든 나쁘다고 들었습니다. – KTrum

+0

예.나는 그것을 좋아할 것이다. 하지만 여전히 귀하의 질문에 대한 의견에 게시 된 IBM 기사를 읽어야합니다. 추가 지식이있을 수 있습니다. – Daniel

+0

이 코드는 IBM 기사의 목록 7과 약간 다릅니다. 두 번째 검사가 최종 위치에 대한 것이지 임시 변수가 생성되는 객체를 저장하는 것이 아니므로 ** 확인 될 수도 있습니다. 이것은 단지 추측이며 컴파일러가'new object()'를'obj = new Object()'와 같은 것으로 최적화하는 것을 볼 수 있습니다. 나는 구현 세부 사항에 의존하는 코드를 가지고 있지 않기 때문에 대답으로 돌아 간다. – unholysampler

0

당신은 createNewObject를 제어 할 수 없다고 말합니다. 따라서 이러한 상황에서는 method1이 올바른 대답이고 나는 그렇게 말한 사람을 상대로 말했습니다. 하지만 createNewObject가 잘못 설계된 것처럼 들린다. 오브젝트가 이미 존재하고 있는지 어떤지를 확인하는 경우, 예외가 발생하지 않고 그 오브젝트를 돌려 주어야합니다. 객체가 존재하는지 여부를 호출자가 확인하도록 요구하고 객체가 존재하는지 여부를 확인하는 함수를 호출하지 않으면 바보입니다. 그것은 동시성 문제와도에 휘발성 변수에 대한 어떤 잠금이 없습니다

private static class LazySomethingHolder { 
    public static Something something = new Something(); 
} 

public static Something getInstance() { 
    return LazySomethingHolder.something; 
} 

:

0

비트를 읽은 후, 나는 정말 최선의 대답은 초기화 주문형 홀더 클래스 이디엄을 사용하는 것입니다 생각 공통 경로.