2010-05-14 2 views
10

나는 액세스 할 수있는 다른 스레드가 원하는 속성을 노출하는 자바 Thread 있습니다getFoo()에 값이 준비 될 때까지 호출자가 차단됩니까?

class MyThread extends Thread { 
    private Foo foo; 
    ... 
    Foo getFoo() { 
    return foo; 
    } 
    ... 
    public void run() { 
    ... 
    foo = makeTheFoo(); 
    ... 
    } 
} 

문제는이 foo 사용할 수있을 때까지이 실행되는 시간에서 약간의 짧은 시간이 걸린다는 것이다. 전화를 걸기 전에 getFoo()으로 전화하면 null이됩니다. 나는 일단 초기화가 발생하면 단순히 차단, 대기 및 값을 얻는다. (foo은 이후로 변경되지 않습니다.) 준비가 될 때까지 수 밀리 초가 걸릴 것이므로이 방법에 익숙합니다.

이제는 wait()notifyAll()으로 만들 수 있으며 95 %의 가능성이 있습니다. 그러나 나는 당신이 어떻게 그것을 할 것인지 궁금해. java.util.concurrent에 원시가 있습니까? 내가 놓친 것입니까?

또는 구조는 어떻게 구성합니까? 예, foo을 휘발성으로 설정하십시오. 예, 내부 잠금 장치 Object에 동기화하고 null이 아닐 때까지 수표를 while 루프에 넣습니다. 내가 놓친 게 있니? 여기

간단한 예 :

답변

14

foo을 한 번만 초기화하면 CountDownLatch이 적합합니다.

class MyThread extends Thread { 

    private final CountDownLatch latch = new CountDownLatch(1); 

    ... 

    Foo getFoo() throws InterruptedException 
    { 
    latch.await(); /* Or use overload with timeout parameter. */ 
    return foo; 
    } 

    @Override 
    public void run() { 
    foo = makeTheFoo() 
    latch.countDown(); 
    } 

} 

래치는 읽기 스레드가 foovolatile 선언되지 않은 경우에도, 스레드에 의해 할당 foo의 가치를 볼 수 있음을 의미하는 volatile 키워드와 동일한 가시성 동작을 제공한다.

+0

당신은 빠르다! LOL – Kiril

0

당신은() 메소드 대기를 (사용) 및 알릴 수 있습니다

http://www.java-samples.com/showtutorial.php?tutorialid=306

+0

을 확실히, 나는 손으로 솔루션을 롤백 할 수 있습니다. 필자는이 필드가 좀 더 복잡하다고 생각합니다. 필드는 '휘발성 (volatile)'이어야하고 notifyAll()이 필요합니다. 그래서 내가 무엇이 필요한지에 대한 의견이나 미리 패키지 된 솔루션이 필요합니다. 권리. –

+0

@Sean 그런 경우에는 DJClayworth의 제안으로 갈 것입니다. –

0

내가 알고있는 것처럼, 동시 물건은 기다릴 할 수 없습니다 명시 적으로 만든 것 너는 당장 그걸 원한다. 그러나 그것이 가능하다면. 귀하의 경우에는 사용할 수있을 때까지 기다려야하므로 유일한 옵션은 wait()입니다. 요컨대, 당신이 그것을 묘사 한 방식이 유일한 올바른 방법 인 것 같습니다.

3

일반적으로 notify() 및 notifyAll()은 원하는 메서드입니다. notify()는 하나의 항목 만 Foo를 만들고 많은 스레드가 그것을 기다리는 경우 위험합니다. 하지만 여기에는 다른 문제가 있다고 생각합니다.

나는 Thread를 Foo를 저장할 장소로 만들지 않을 것이다. 그렇게하면 Foo가 생성 된 후에 스레드를 유지해야합니다. Foo를 저장하는 또 다른 객체를 만들고 거기에 쓰는 쓰레드를 만들어 보자.

그렇다면 getFoo()는 foo를 테스트하고 null이 아닌 경우에만 대기합니다 (자체와 foo setter를 동기화하는 것을 잊지 마십시오).

+0

+1 MyThread –

+0

에서 foo를 추출하려면 동의하지 않았 으면합니다. 이 경우 실제로 run() 내에서 객체를 만들어야합니다. 이상한 요구를 가진 GUI 앱입니다. –

0

지연 초기화는 옵션입니까?

synchronized Foo getFoo() { 
    if (foo == null) 
     foo = makeFoo(); 
    } 
    return foo; 
} 
+0

다른 스레드에서 foo를 작성해야하기 때문에이 경우가 아닙니다. –

0

는 시도 CountDownLatch : 나는 모든 waitnotify을 기대하기 때문에 wait/notifyAll은 아닌 것 같다

class MyThread extends Thread { 
    private volatile CountDownLatch latch; 
    private Foo foo; 
    MyThread(){ 
     latch = new CountDownLatch(1); 
    } 
    ... 
    Foo getFoo() { 
    latch.await(); // waits until foo is ready 
    return foo; 
    } 
    ... 
    public void run() { 
    ... 
    foo = makeTheFoo(); 
    latch.countDown();// signals that foo is ready 
    ... 
    } 
} 

. 한 번 알림을 보내고 다시 알림을 보내지 않으려면 getFoo을 호출하는 다른 스레드는 foo이 초기화 될 때까지 차단되거나 이미 초기화 된 경우 foo이됩니다. 한 푸 기다리는 스레드 하나가 좀 더 생산 및/또는 더 많은 소비자를 기본 옵션의 경우에 SynchronousQueue를 사용하는 것을 생산하고있는 경우

+0

사실, '래치'는 '최종'또는 '휘발성'이 아니기 때문에 여기에 미묘한 버그가 있습니다. 다른 스레드에 "눈에 보이는"것은 아닙니다. 실제로는 문제가되지 않을 수도 있지만 보증으로 인해 비용이 많이 들지는 않습니다. – erickson

1

나는, 더 구체적으로 java.util.concurrentBlockingQueue 년대 중 하나를 사용하십시오 LinkedBlockingQueue이지만 다른 구현이 응용 프로그램에 더 적합 할 수 있습니다. 코드는 다음이된다 :

class MyThread extends Thread { 
    private SynchronousQueue<Foo> queue = new SynchronousQueue<Foo>(); 
    ... 
    Foo getFoo() { 
    Foo foo; 
    try { 
     foo = queue.take(); 
    } 
    catch (InteruptedException ex) { 
     ...stuff ... 
    } 
    return foo; 
    } 
    ... 
    public void run() { 
    ... 
    foo = makeTheFoo(); 
    try { 
     queue.put(foo); 
    } 
    catch (InteruptedException ex) { 
     ...stuff ... 
    } 
    ... 
    } 
} 
+0

SynchronousQueue는 많은 경우에 편리한 구조이지만 여기서는 요구 사항에 매우 잘 부합하지 않습니다.'foo'는 한 번 초기화되고 여러 번 읽힐 수 있습니다. 이것은 단지'foo'가 한번 읽힐 수있게합니다. 재산을 읽으려는 다른 시도는 영원히 차단 될 것입니다. – erickson

+0

예, 맞습니다. "한 번만 초기화되었습니다"부분을 놓쳤습니다. 나는 이것이 더 생산자/소비자 상황이라고 생각했다. –

0

어쩌면 내 공상 FutureValue 클래스를 시도 ...

import java.util.concurrent.CountDownLatch; 

public class FutureValue<T> { 
private CountDownLatch latch = new CountDownLatch(1); 
private T value; 

public void set(T value) throws InterruptedException, IllegalStateException { 
    if (latch.getCount() == 0) { 
     throw new IllegalStateException("Value has been already set."); 
    } 
    latch.countDown(); 
    this.value = value; 
} 

/** 
* Returns the value stored in this container. Waits if value is not available. 
* 
* @return 
* @throws InterruptedException 
*/ 
public T get() throws InterruptedException { 
    latch.await(); 
    return value; 
} 

} 

// Usage example 
class MyExampleClass { 
@SuppressWarnings("unused") 
private static void usageExample() throws InterruptedException { 
    FutureValue<String> futureValue = new FutureValue<>(); 

    // the thread that will produce the value somewhere 
    new Thread(new Runnable() { 

     @Override 
     public void run() { 
      try { 
       futureValue.set("this is future"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    }).run(); 

    String valueProducedSomewhereElse = futureValue.get(); 
} 
} 
관련 문제