2012-04-23 5 views
1

이 개체 풀이 여러 스레드에서 가시성 문제를 일으킬 수 있습니까? 내가 특별히 실행 순서의이 종류에 대해 궁금 해요 :이 비동기 개체 풀 구현은 안전합니까?

  • 스레드 A - obtainObject()를
  • 스레드 A가 - releaseObject()
  • - 객체 수정
  • 스레드 A (visibleState = 42라고 할 수 있습니다)
  • 스레드 B - 관련이없는 무언가를하거나
  • 스레드 B 다이 - - 객체를 수정
  • (말 visibleState = 1 수) obtainObject()는
  • 스레드 A (그냥 발표 한 객체를 가져)
  • 스레드 B - 인쇄 visibleState
  • 스레드 B 개체 - releaseObject()를

스레드 A에서 변경이 가능한 상태 자체를 수정 한 B 후 B 스레드로 표시 될 수 있을까? (실제로는 발생하지 않지만, JLS/Javadoc이 이것을 보장하는지, 그리고 어떻게 보장되는지는 알 수 없습니다.)

필수 항목 만 보여주기 위해 아래 코드를 제거했습니다. 나는 물건을 만들기 위해 generification과 공장을 버렸다. 수영장에 의해 관리

import java.util.concurrent.atomic.AtomicInteger; 
import java.util.concurrent.atomic.AtomicReference; 

public class ObjectPool { 

/** Maximum number of objects to be kept in pool */ 
int maxPoolSize = 10; 

/** First object in pool (LIFO queue) */ 
final AtomicReference<PoolableObject> firstObject = new AtomicReference<PoolableObject>(); 

/** How many objects are currently in the pool */ 
final AtomicInteger poolSize = new AtomicInteger(); 

/** Gets an object from the pool. If no object is available 
* from the pool, a new object is created and returned */ 
public PoolableObject obtainObject() { 
    while (true) { 
     PoolableObject object = firstObject.get(); 
     if (object == null) 
      break; 
     if (firstObject.compareAndSet(object, object.next)) { 
      poolSize.decrementAndGet(); 
      return object; 
     } 
    } 
    // no more objects in pool, create a new object 
    return new PoolableObject(); 
} 

/** Returns an object to the pool. */ 
public void releaseObject(final PoolableObject object) { 
    while (true) { 
     if (poolSize.get() >= maxPoolSize) 
      break; 
     final PoolableObject first = firstObject.get(); 
     object.next = first; 
     if (firstObject.compareAndSet(first, object)) { 
      poolSize.incrementAndGet(); 
      break; 
     } 
    } 
} 

} 

개체는이 클래스에서 상속로되어있다 : 이러한 속성 중

public class PoolableObject { 

/** Links objects in pool in single linked list. */ 
PoolableObject next; 

public int visibleState; 

} 
지금까지 가시성이 간다
+0

실제로 대기열이 없습니다. –

+0

firstObject에 대한 의견을 참조 하시겠습니까? 어쩌면 내 영어가 실패 하긴하지만, 하나의 연결된 목록을 합리적으로 '대기열'이라고 부를 수 있다고 생각합니다. – Durandal

+1

아마도 대기열처럼 사용되지는 않습니다. 크기가 1 인 ['ArrayBlockingQueue'] (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ArrayBlockingQueue.html)는 올바르게 사용하는 것이 훨씬 쉽습니다. 또는 [ 'SynchronousQueue'] (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/SynchronousQueue.html)를 참조하십시오. –

답변

0

, AtomicReference has the same memory barrier properties as a volatile variable.

는 그 스레드의 모든 작업 전에 휘발성 변수 에 쓰기 "happen-before"로 쓰는. 다른 말로 표현하자면, 소스 코드가 휘발성 쓰기 전에 일어나는 동작을 보여 주면 JITC가 휘발성 쓰기 후에 해당 동작을 다시 정렬 할 수 없습니다. 따라서 질문에 언급 된 시나리오는 문제가되지 않습니다. 해제되기 전에 만들어진 객체의 변경 사항은 이후에 객체를 캡처하는 다른 스레드에서 볼 수 있습니다.

그러나이 풀이 작동하는 방식을 완전히 따르지는 않으며 다른 문제가있을 수 있습니다. 특히, 나는 firstObjectpoolSize 사이의 원 자성의 부족이 안전하다는 것을 확신하지 못합니다. 스레드는 synchronized이 아니기 때문에 이러한 변수를 일관성없는 상태로 볼 수 있습니다. 나는 그것이 중요하더라도 말할 수 없다.

+0

아, AtomicReference의 '휘발성'장벽 속성을 놓쳤습니다. 본질적으로, A가 AtomicReference에 액세스하기 전에 수행 한 메모리 수정은 AtomicRef에 대한 액세스 후에 B가 볼 수 있도록 보장됩니다. 그게 내 질문을 완벽하게 지 웁니다. poolSize에 관해서는 그렇습니다. 실제 풀링 된 객체 목록과 완전히 동기화되지 않았습니다. 내 목적을 위해서는 필연적 인 부정확성을 고려해야하지만 그 효과는 설정 한 풀 크기가 정확하게 준수되지 않아야하므로 나를 귀찮게하지 않아야합니다. – Durandal

+1

@Durandal :'AtomicStampedReference'를 사용하여 참조와 크기를 저장함으로써'firstObject'와'poolSize' 사이의 불일치 가능성을 제거 할 수 있습니다. – axtavt