2015-01-22 3 views
0

SomeModel 클래스의 인스턴스를 생성하는 정적 메서드를 사용하는 Factory 클래스가 주어지면 다중 스레드 환경에서 실행됩니다.내 코드 스레드를 안전하게 만드는 방법은 무엇입니까?

  1. 시작
  2. 정지
  3. createSomeModel

와에있는 state 필드 :

  1. 을 시작 단열성이 Factory은 세 가지 방법이있다

    public class Factory { 
    
        enum State{ 
         shutted, starting, starting_failed, started, shutting, shutting_failed 
        } 
    
        State state; 
    
        public static void start(){ 
         // can only be invoked when state=shutted 
        } 
    
        public static void stop(){ 
         // can only be invoked when state=started 
        } 
    
        public static void restart(){ 
         stop(); 
         start(); 
        } 
    
        public static SomeModel create(){ 
         // can only be invoked when state=started 
        } 
    } 
    

    내 요구 사항은 다음과 같습니다 :

여기 Factory 클래스의 현재 디자인의 shutting_failed 종료

  • 시작 starting_failed create 메소드는 호출 할 수 있습니다 때 state==startedstart 메서드는 state==shutted || state == shutting_failed 및일 때만 호출 할 수 있습니다.메서드는 state==started|| state == starting_failed 일 때만 호출 할 수 있습니다.

    이것이 스레드 동기화 문제와 관련이 있다는 것을 알고 있지만 스레드 지식에 대한 확신이 없습니다. 도와주세요.

  • +1

    'synchronized'블록을 사용하십시오. –

    +0

    'Factory'는 이것을 위해'static' 메소드를 필요로합니까?모든 응용 프로그램을 통해 'Factory' 인스턴스를 하나만 갖고 싶기 때문에 클라이언트가'start' 메소드를 호출 할 필요가 없습니다. –

    +0

    @LuiggiMendoza 네, 맞습니다. 정적 메서드를 사용하는 것이 좋지 않습니까? 싱글 톤 패턴을 사용해야합니까? – CaiNiaoCoder

    답변

    0

    이 메서드는 메서드 생성을 동기화하지 않으므로 병목 현상이 발생하지 않습니다. 그리고 당신이 멈추고있는 동안에는 새로운 사형 집행을 허용하지 않습니다. 경쟁 조건을 피하기 위해 "if state == started"라는 이중 점검이 필요합니다.

    아마 모든 모델 완성을 기다리는 Thread.sleep을 사용하는 것보다 나은 솔루션이 있지만 쉽게 수행하는 방법을 알지 못합니다.

    희망이 도움이 될 수 있습니다.

    enum State{ 
        shutted, starting, starting_failed, started, shutting, shutting_failed 
    } 
    
    private Factory() { 
        // singleton: no more than 1 instances allowed 
    } 
    
    public static Factory getInstance() { 
        return instance; 
    } 
    
    private static final Factory instance = new Factory(); 
    private final AtomicInteger threadsCreatingModel = new AtomicInteger(); 
    private volatile State state; 
    
    
    public synchronized void start(){ 
        if(state != State.shutted) { 
         throw new RuntimeException("Can only be invoked when state=shutted"); 
        } 
        state = State.starting; 
        // TODO: task 
    } 
    
    public synchronized void stop() throws InterruptedException { 
        if(state != State.started) { 
         throw new RuntimeException("Can only be invoked when state=started"); 
        } 
        state = State.shutting; 
    
        // wait all threads that are creating SomeModel 
        while (threadsCreatingModel.intValue() > 0) { 
         Thread.sleep(500); 
        } 
        // TODO: task 
    } 
    
    public SomeModel create(){ 
        if(state == State.started) { 
         threadsCreatingModel.incrementAndGet(); 
         if(state == State.started) { 
          // TODO: task 
         } 
         threadsCreatingModel.decrementAndGet(); 
        } 
    } 
    
    +0

    하지만 '작성'방법은 어떨까요? 이 방법을 동기화하면 성능이 저하됩니다. – CaiNiaoCoder

    +0

    나는 creater 메소드가 syncrhonize를 필요로하지 않으며, stop이 모든 메소드 생성을 기다리기 위해서 코드를 변경했다. – ARIS

    0

    나는 정적 방법을 전혀 사용하지 말 것을 제안합니다.

    대신에 Factory 클래스의 개체를 만들고 모든 메서드를 synchronized으로 설정합니다.

    public enum State{ 
        shutted, starting, starting_failed, started, shutting, shutting_failed; 
    } 
    
    public class Factory { 
        private State state; 
    
        public synchronized void start(){ 
         if(state != State.shutted) { 
          throw new RuntimeException("Can only be invoked when state=shutted"); 
         } 
         // do task 
        } 
    
        public synchronized void stop(){ 
         if(state != State.started) { 
          throw new RuntimeException("Can only be invoked when state=started"); 
         } 
         // do task 
        } 
    
        public synchronized void restart(){ 
         stop(); 
         start(); 
        } 
    } 
    

    희망이 있습니다.

    +0

    하지만 '작성'방법은 어떨까요? 이 방법을 동기화하면 성능이 저하됩니다. – CaiNiaoCoder

    0

    동기화 할 필요가 없습니다. State에는 volatile 또는 AtomicReference를 사용하십시오. 휘발성을 사용하는 샘플 예제를 제공하고 있습니다. 기본 유형을 사용하는 것이 가장 좋으므로 다른 상태에 대해 int 값을 추가해야합니다. 열거 형의 서수를 사용할 수 있지만이 방법은 약간 명확합니다. 그렇지 않으면 AtomicReference를 사용할 수 있습니다.

    public class Factory { 
        private static volatile int factoryState; 
    
        public synchronized void updateFactoryState(State newState, State ... expectedStates){ 
         for (State state : expectedStates) 
          if(factoryState == State.shutted.getStateVal()){ 
           factoryState = newState.getStateVal(); 
         } 
        } 
    
        public void start(){ 
         try{ 
          updateFactoryState(State.starting, State.shutted, State.shutting_failed); 
          System.out.println("steps to start the factory"); 
          //someExpensiveStartupMethod(); 
         }catch (Exception e){ 
          updateFactoryState(State.starting_failed, State.starting); 
         } 
         updateFactoryState(State.started, State.starting); 
        } 
    
        public void stop(){ 
         try{ 
          updateFactoryState(State.shutting, State.started, State.starting_failed); 
          System.out.println("steps to stop the factory"); 
          //someExpensiveStopFactoryMethod(); 
         }catch (Exception e){ 
          updateFactoryState(State.shutting_failed, State.shutting); 
         } 
         updateFactoryState(State.shutted, State.shutting); 
        } 
    
        public void restart(){ 
         stop(); 
         start(); 
        } 
        public static SomeModel create(){ 
         if(factoryState == State.started.getStateVal()) { 
          System.out.println("Create Model"); 
         } else{ 
          throw new RuntimeException("Can not create Model.Factory not in started state."); 
         } 
         return null; 
        } 
    
        enum State{ 
         shutted(0), starting(1), starting_failed(2), started(3), shutting(4), shutting_failed(5); 
         private final int stateVal; 
    
         State(int i) { 
          stateVal = i; 
         } 
    
         public int getStateVal() { 
          return stateVal; 
         } 
        } 
    
        class SomeModel {} 
    } 
    
    +1

    start(), stop(), create()에 경쟁 조건이 포함되어 있기 때문에이 구현이 깨졌습니다. 일반적으로 휘발성은 공유 값을 변경하지 않고 읽거나 원 래 값을 원자 적으로 변경하려는 경우에만 적용되며 현재 값을 고려하지 않아도됩니다. –

    +1

    친절한 요청으로 다시 한번 휘발성을 읽으십시오. 스레드 안전의 가장 높은 수준은 변경되지 않음에서 비롯됩니다. 불변의 변수 (휘발성 또는 임의의 것)를 읽는 데있어서 안전이 필요 없으며 휘발성도 모든 원시 타입에 대한 원자 적 연산을 제공합니다. 또 다시 휘발성을 재검토하십시오. – Mak

    +1

    해당 코드의 문제 : operation a)'if (factoryState == ...)'; 작업 b)'factoryState = nextState'. a), b) 이전에 스레드가 일시 중지 된 경우 스레드가 작업을 계속하기 전에 factoryState가 예기치 않은 값을 수신 할 수 있습니다. 그것은 경쟁 조건입니다. –

    관련 문제