2011-01-21 5 views
5

나는, 자바루프 동기화 교착 상태

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 
    public void setValue(int value) { 
     this.value = value; 
    } 
    public void decrement() { 
     this.value--; 
    } 
    public int getValue() { 
     return this.value; 
    } 
} 

public class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r=0; r<Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized(counter) { 
       counter.decrement(); 
       counter.notifyAll(); 
      } 
      synchronized(sync) { 
       try { 
        sync.wait(); 
       } 
       catch (Exception ex) {} 
      } 

     } 
    } 
} 

public class Simulation extends Thread { 

    public static final int THREAD_NUM = 5; 
    public static final int ROUND_NUM = 5; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i=0; i<THREAD_NUM; i++) { 
      Cell c = new Cell(sync,counter); 
      c.start(); 
     } 

     for (int i=0; i<ROUND_NUM; i++) { 
      synchronized(counter) { 
       while(counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } 
        catch (Exception ex) {} 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized(sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 

목적은 각 셀 스레드에서 루프의 다음 반복 실행을 방지하는 것입니다에 다음과 같은 클래스를 가지고있다. 내 솔루션으로 인해 때때로 교착 상태가 발생합니다. 이유를 이해할 수 없습니다. 도와주세요

+2

사이드 노트 : 명시 적으로'Thread','wait' 및'notify' (Java5 이상 사용)를 사용해야 할 필요가 없다면 ['CountDownLatch'] (http 대신 //download.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html)을 참조하십시오. –

답변

3

코드에서 sync.notifyAll()이 실행되면 모든 셀 스레드가 sync.wait()이 될 것이라고 보장 할 수 없습니다. 이것은 기다리기 위해 sync에 대한 잠금을 잡아야하는 마지막 셀 스레드 (예제의 다섯 번째 스레드)를 나타냅니다. 그러나 시뮬레이션 스레드는 모든 사람이 기다리고 있는지 확인하지 않고도 똑같은 것을 시도하고 있습니다. 경쟁 조건으로 인해 시뮬레이션은 때때로 마지막 셀이 동일 작업을 수행하고 대기하기 전에 잠금 장치를 잡을 수 있습니다.

마지막 셀이 대기 중이므로 알림을받지 못하므로 모든 문제가 발생합니다. 각 synchronized (sync) 블록의 첫 번째 줄에 System.out.println()을 추가하고 "동기화 대기 중"및 "동기화 알림"을 ​​적절히 작성하여 테스트 할 수 있습니다. 통지 할 때 동기화 대기중인 스레드는 4 개뿐입니다. 시뮬레이터의 통지가 중첩 Cell#run()에있는 두 개의 동기화 된 블록이있을 때 모든 사람을 만들려면

기다리고 있습니다 :

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 

    public void setValue(int value) { 
     this.value = value; 
    } 

    public void decrement() { 
     this.value--; 
    } 

    public int getValue() { 
     return this.value; 
    } 

    public static void main(String[] args) { 
     new Simulation().start(); 
    } 
} 

class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r = 0; r < Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized (sync) { 
       synchronized (counter) { 
        counter.decrement(); 
        counter.notifyAll(); 
       } 
       try { 
        sync.wait(); 
       } catch (Exception ignored) {} 
      } 


     } 
    } 
} 

class Simulation extends Thread { 

    public static final int THREAD_NUM = 900; 
    public static final int ROUND_NUM = 30; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i = 0; i < THREAD_NUM; i++) { 
      Cell c = new Cell(sync, counter); 
      c.start(); 
     } 

     for (int i = 0; i < ROUND_NUM; i++) { 
      synchronized (counter) { 
       while (counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } catch (Exception ex) { 
        } 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized (sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 
+0

확실히 더 잘 작동하지만 더 많은 스레드를 만들수록 더 많은 미완성 프로그램을 얻을 수 있습니다. 900 개의 스레드와 30 번의 반복을 고려하면 10 개의 최근 재판 중 2 번만 성공적으로 완료되었습니다. – marooou

+0

더 이상 교착 상태에 빠질 이유가 없습니다. 실행중인 코드를 게시 한 코드는 무엇입니까? –

+0

정확한 코드가 아닙니다. 내 코드에서는 주석이 일부 작업으로 대체되지만 Cell 클래스에서이 모든 내용을 제거하면 문제가 해결되지 않습니다. 그리고 더 이상 무엇이 더 이상한 지, 내가 코멘트 대신에 잠을 자면, 프로그램은 완벽하게 작동합니다 ... 항상. – marooou

5

먼저 Counter 클래스 대신 AtomicInteger 클래스를 사용할 수 있습니다. AtomicInteger 클래스는 스레드로부터 안전하기 때문에 decrementAndGetincrementAndGet과 같은 원 자성 액션을 사용할 수 있습니다.

각 셀 스레드가 완료 될 때까지 기다리는 기능을 수행하려면 이전 메모에서 언급 한 CountDownLatch을 사용하거나 CyclicBarriers과 같은 동시 개체도 모든 셀 스레드가 장벽에 참여할 때까지 실행을 중단 할 수 있습니다. 이러한 병행 객체 중 일부를 통해 여러 스레드를 제어하는 ​​것이 더 쉬워야합니다. 평범한 동기화를 사용하면 모든 것이 잘 작동하는지 확인하기 위해 더 많은 코딩과 사고가 필요합니다.

2
셀 스레드가 실제로 될 것이라고 당신이 어떤 보장을하지 있기 때문에 귀하의 코드가 교착 상태 수

notifyAll이 발생하는 시점의 wait() 블록에서. 다음은이 문제를 일으킬 수있는 일련의 이벤트입니다.

  1. 시뮬레이션은 모든 스레드를 시작하고 0 값을 대기하는 블록입니다.
  2. 각 스레드가 감소하고 counter.notifyAll을 호출 한 다음 시간 조각이 손실 됨
  3. 주 스레드가 알림을 받고 깨어나고 카운터가 0 인 것으로 확인하고 sync.notifyAll을 호출하고 맨 위로 루프를 돌립니다. , 무기한 대기합니다.
  4. 각 스레드는 순서대로 시간 조각이 지정되고 wait()로 진행되며 무기한 대기합니다.
0

멋진 예! 정의에 따르면 스레드가 두 개 이상의 잠금을 동시에 보유하고 다른 스레드가 동일한 잠금을 다른 순서로 획득하려고 시도 할 때만 발생할 수 있으므로 교착 상태가 아닙니다.
여기에있는 문제는 Cell 객체에서 발생하는 가짜 깨우기 (wake-ups)로 인한 것 같습니다 (시뮬레이션 객체에서 가짜 깨우기가 발생하면 wait()가 루프에서 호출 될 때 아무런 효과가 없습니다. 다시 입력 대기).
셀에서 가짜 깨우기가 발생하면 추가 감소가 발생합니다. 이것은 차례로 테스트 while(counter.getValue() != 0)을 밟을 것입니다.
해당 조건을 while(counter.getValue() >= 0)으로 변경하면 '교착 상태가 사라집니다. 작동하는지 알려주십시오.

+0

아니, 나는 그렇게 생각하지 않는다. 나는 그가 어떻게 든 알리는 것 중의 1 개를 그리워한다라고 생각한다, 나는 어떻게 꽤 이해하는지 모른다. 하지만 막히면 카운터는 여전히 0입니다. –

+0

그건 내 첫 아이디어 였지만 MK가 옳습니다. 트릭을하지 않습니다. – marooou

+0

나는 계속 노력했지만 라이브 록을 얻을 수 없었다. –

0

이것은 교착 상태가 아닙니다. 주 스레드는 카운터에 대한 알림을 놓칠 수 있으며 0으로 읽은 후 counter.wait()에 걸릴 수 있습니다. jstack JDK 도구를 사용하여 이와 같은 상황에서 수행중인 스레드를 분석합니다.