2013-08-09 2 views
6

나는 이런 식으로 어느 정도 서버를 가지고 : 당신이 볼 수 있듯이"궁극적으로 최종"변수에 대한 스레드 안전하지만 빠른 액세스?

class Server { 

    private WorkingThing worker; 

    public void init() { 
     runInNewThread({ 
      // this will take about a minute 
      worker = new WorkingThing(); 
     }); 
    } 

    public Response handleRequest(Request req) { 
     if (worker == null) throw new IllegalStateException("Not inited yet"); 
     return worker.work(req); 
    } 

} 

, 요청과 서버를 INITING 스레드를 처리하는 스레드가 있습니다. 초기화가 완료되기 전에 요청이 들어올 수 있으므로 IllegalStateException으로 확인하십시오. 이제

는, 내가 노동자가 휘발성 수 있도록해야 할 것, (그래서 요청 처리기 스레드가 부실 표시되지 않습니다 null -valued, 단지 초기화 후 worker의 버전)이 스레드가 안전하게 그것을 동기화하거나 일부 이러한.

그러나 init이 완료된 후에 worker은 다시 변경되지 않으므로 사실상 최종입니다. 따라서 발생할 수있는 잠금 충돌이 낭비되는 것처럼 보입니다. 그래서 내가 여기서 할 수있는 가장 효율적인 것은 무엇입니까?

이제 실용적인 의미로는 중요하지 않다는 것을 알았습니다. (네트워크 요청을 읽는 등 무거운 짐을 지거나, 단일 잠금이 중요한 것은 무엇입니까?)하지만 호기심을 알고 싶습니다. .

+0

'synchronized'키워드를 사용하면 많은 비용이 소요됩니다. 가장 좋은 방법은'worker' 변수를'static' 변수로 만드는 것이라고 생각합니다. 이렇게하면 메모리에 예약 된 공간이 할당되고 스레드는 항상 해당 공간을 볼 것입니다. 그냥 생각이 맞지 않을 수도 있습니다. –

+0

'worker.work (req)'의 호출은 안전합니까? 난 그냥 'worker' 초기화 될 때까지 호출 스레드를 차단할 필요가 있는지 확인하는 것입니다. 그 맞습니까? –

+0

@ViktorSeifert 예, 일단 worker가 있으면 스레드로부터 안전합니다. –

답변

3

휘발성에 대한 참고 사항 : 변수 휘발성을 표시하는 것은 동기화를 사용하는 것보다 저렴합니다 (잠금을 포함하지 않음). 일반적으로 눈에 띄지 않을만큼 저렴합니다. 특히, x86 아키텍쳐에서 휘발성 변수를 읽는 것은 비 휘발성 변수를 읽는 것보다 비용이 많이 들지 않습니다. 그러나 휘발성 메모리에 쓰기는 더 비싸고 변수가 휘발성이라는 사실은 컴파일러의 최적화를 방해 할 수 있습니다.

따라서 휘발성을 사용하는 것이 시나리오에서 최상의 성능/복잡성 비율을 제공하는 옵션 일 수 있습니다.

많은 대안이 없습니다. 결국 그것은 당신의 근로자의 안전한 출판을 보장하는 것으로 귀결됩니다. 그리고 안전 출판 관용구는 다음과 같습니다

  • 모두가
에 액세스 동기화와 같은 휘발성 인스턴스
  • 에 대한 참조를 표시하는 최종
  • 같은 인스턴스에 대한 참조를 표시 정적 initialiser
  • 에서 인스턴스를 초기화하는

    마지막 두 옵션 만 사용할 수 있으며 휘발성을 사용하는 것이 더 효율적입니다.

  • +0

    아, 휘발성은 "이 변수에 잠금을 설정하고이 변수에 액세스 할 때 항상 동기화"하기 위해 구문 상 설탕이라고 생각했습니다. 그렇지 않다는 것을 알고 있습니다. –

    +0

    정적 초기화는 동기화 된 블록에서 수행됩니다. 그것이 가시성 (및 기타 속성)이 보장되는 방법입니다. – erickson

    1

    근로자가 휘발성이이라고 선언해야합니다.

    때문에 두 가지 이유

    1. 당신으로 인해이 아닌 Null 참조 불완전 노동자 개체를 구축하기 위해 볼 수 재정렬 및 ​​가시성 효과보다이 같은 휘발성 선언하지 않는 경우. 따라서 에 바람직하지 않은 영향을 미칠 수 있습니다. 이것은 모든 최종 변수를 사용하여 작업자를 불변으로 만들면 발생하지 않습니다.

    2. 주 스레드가 오랫동안 작업자 개체의 null이 아닌 참조를 보지 못할 수도 있습니다. 그러니 피하십시오.노동자는 점 2 당신이 휘발성해야보다 불변의 경우

    그래서 결론. 예기치 않은 결과는 항상 피하십시오. 휘발성이라고 선언하면 이러한 문제가 해결됩니다.

    0

    avaya.util.concurrent.atomic.AtomicReference null, iether throw 또는 wait 및 init 시도가 ​​빠르면 기다려야합니다. 초기 요청에 대한

    1

    를 사용하여 간단한 잠금 : 노동자에 액세스 사이에 동기화 지점이 있기 때문에

    public synchronized void init() { 
        if(worker!=null) return; 
        runInNewThread({ 
         synchronized(Server.this){ 
          worker = new WorkingThing(); 
          Server.this.notify(); 
         } 
        }); 
        this.wait(); 
    } 
    
    public Response handleRequest(Request req) { 
        if (worker == null) synchronized(this) { 
         this.wait(); 
        } 
        return worker.work(req); 
    } 
    

    이 작동합니다.

    +1

    루프 밖에서 절대로 기다리지 마십시오. 알림 신호를 놓친 경우 영원히 기다릴 수 있습니다. 그리고 하나 이상의 스레드가 기다리고있을 수도 있습니다 => notifyAll을 사용하십시오. 그리고 init이 끝날 때까지 기다리는 것은 없습니다. – assylias

    +0

    아아, 오른쪽 - handleRequest synchronized (this) 블록에서 이중 잠금 검사 여야합니다. –