2014-05-24 2 views
1

스레드의 myList라는 arraylist가 있는데 모두 Runnable 인터페이스를 구현하는 myRunnable 클래스의 인스턴스로 작성된다는 것을 가정합니다. 스레드는 myRunnable의 run() 메소드에서 실행할 동일한 코드를 공유합니다. 이제 runTable 인터페이스를 구현하는 otherRunnable 클래스의 인스턴스로 생성 된 singleThread라는 또 다른 단일 스레드가 있다고 가정 해 보겠습니다.Java의 단일 스레드와 관련하여 여러 스레드 세트를 동기화하는 방법

이러한 스레드에 대해 해결해야하는 동기화 문제는 다음과 같습니다. myList의 모든 스레드가 특정 시점까지 코드를 실행해야합니다. 일단이 시점에 도달하면, 그들은 잠을 자다. myList에있는 모든 스레드와 모든 스레드 만 잠자기하면 singleThread를 잠에서 깨워야합니다 (singleThread는 이미 잠 들어있었습니다). 그런 다음 singleThread는 자체적 인 작업을 실행하고 완료되면 myList의 모든 스레드가 잠에서 깨어 야합니다. 코드가 while (true)로 감싸 졌다고 상상해보십시오. 따라서이 과정은 계속해서 반복되어야합니다.

class myRunnable extends Runnable 
{ 
    public static final Object lock = new Object(); 
    static int count = 0; 

    @override 
    run() 
    { 
    while(true) 
    { 
     //do stuff 
     barrier(); 
     //do stuff 
    } 
    } 

    void barrier() 
    { 
    try { 
     synchronized(lock) { 
      count++; 
      if (count == Program.myList.size()) { 
      count = 0; 
      synchronized(otherRunnable.lock) {   
       otherRunnable.lock.notify(); 
      } 
      } 
      lock.wait(); 
     } 
    } catch (InterruptedException ex) {} 
    } 
} 

class otherRunnable extend Runnable 
{ 
    public static final Object lock = new Object(); 

    @override 
    run() 
    { 
    while(true) 
    { 
     try { 
     synchronized(lock) { 
      lock.wait(); 
     } catch (InterruptedException ex) {} 

     // do stuff 

     try { 
     synchronized(myRunnable.lock) { 
      myRunnable.notifyAll(); 
     } 
    } 
    }   
} 

class Program 
{ 
    public static ArrayList<Thread> myList; 

    public static void main (string[] args) 
    { 
    myList = new ArrayList<Thread>(); 

    for(int i = 0; i < 10; i++) 
    { 
     myList.add(new Thread(new myRunnable())); 
     myList.get(i).start(); 
    } 
    new Thread(new OtherRunnable()).start(); 
    } 
} 

기본적으로 내 생각은 myList에 스레드 단지를 제외하고이 나올 때까지 기다리는 것이 있는지 확인하기 위해 카운터를 사용하는 것입니다 : 여기

난 그냥 동기화 문제를 해결하는 시도를 포함 설명한 상황의 예입니다 카운터를 0으로 재설정하는 카운터를 증가시키는 마지막 스레드는 lock으로 알리는 것으로 singleThread를 깨우고, 마지막 스레드는 myRunnable.lock을 기다리면서 sleep로 이동합니다. 좀 더 추상적 인 레벨에서, myList의 쓰레드가 임계점에서 실행을 멈추도록 장벽을 사용하고, 마지막으로 쓰레기를 치는 마지막 쓰레드가 singleThread를 깨우고 잠을 자면, singleThread는 그 물건을 만듭니다 끝나면 장벽의 모든 스레드를 깨워서 다시 계속할 수 있습니다.

내 문제는 내 논리에 결함이 있다는 것입니다 (아마도 그 이상이 있습니다). 장벽에 부딪 치는 마지막 스레드가 otherRunnable.lock에 알릴 때 마지막 스레드가 myRunnable.lock에서 대기를 실행하고 잠자기 상태가되기 전에 즉각적인 컨텍스트 전환이 발생할 수있는 기회가 생겨 singleThread에 cpu가 제공됩니다. 그런 다음 singleThread는 모든 내용을 실행하고 myRunnable.lock에서 notifyAll을 실행하며 myList의 모든 스레드는 wait 명령을 아직 실행하지 않았으므로 마지막 스레드가 장벽에 도달하는 것을 제외하고 각성 상태가됩니다. 그런 다음 모든 스레드는 다시 작업을 수행하고 장벽을 다시 뚫습니다. 그러나 앞에서 언급 한 마지막 스레드가 결국 다시 예약되고 wait가 실행되기 때문에 카운트가 myList.size()와 같지 않습니다. singleThread는 차례대로 첫 번째 줄에서 wait를 실행하므로 결과적으로 모든 사람이 잠자고 교착 상태가 발생합니다.

내 질문은 : 이전에 원하는 동작을 달성하기 위해 이러한 스레드를 동기화하는 좋은 방법은 무엇입니까? 그러나 교착 상태가 안전 할 때까지 동시에 어떻게해야합니까 ??

+0

tl; dr 해결하려는 문제에 대한 더 높은 수준의 설명을 제공 할 수 있습니까? - 이미 java.util.concurrent에 좋은 해결책이있을 것입니다. –

+0

어쩌면 내 질문에 아직 명확하지 않아서, 간단하게 다시 설명하려고 노력할 것입니다. n 개의 스레드와 다른 스레드 x가 있습니다. 다음과 같은 동작을 수행하는 방법을 알고 싶습니다. 먼저 n 개의 스레드가 중요한 코드 줄까지 실행 된 다음 실행을 중단하고 잠자기 상태가되면 x가 활성화되고 실행을 시작한 다음 x가 n 개의 스레드를 깨우고 이동합니다. 절전 모드를 종료하면 핵심 코드 줄, 실행 중지, 절전 모드, x가 다시 활성화 될 때까지 n 개의 스레드가 다시 실행됩니다. 심볼에서 : n -> x -> n -> x .... –

+0

당신이 취할 수있는 접근법 중 하나는'OtherRunnable'이 all all을 호출하기 전에 다른 모든 스레드의 상태를 검사하도록하는 것입니다. 'OtherRunnable'이'MyRunnable'의 모든 쓰레드가 대기 상태가 아니라면, 잠자기하고 모든'MyRunnable' 쓰레드가 대기 상태가 될 때까지 다시 검사하십시오. [Thread.State] (http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html)를 참조하십시오. (나에게 해커처럼 보이기 때문에이 글을 대답으로 올리지는 않았지만, 일을 끝내야한다고 생각합니다.) – Tyler

답변

3

귀하의 의견에 따르면 CyclicBarrier과 같은 소리가 정확하게 사용자의 요구에 맞을 것입니다. 워드 프로세서 (강조 광산)에서 :

그 동기 원조는 모든 스레드의 세트가 공통의 장벽 포인트에 도달하기 위해 서로를 기다릴 수 있습니다. CyclicBarriers는 때때로 서로 기다려야하는 고정 된 크기의 스레드 파티를 포함하는 프로그램에서 유용합니다. 장벽은주기라고하며 대기 스레드가 해제 된 후 다시 사용할 수 있습니다.

불행히도, 나는 그들 자신을 사용하지 않았기 때문에, 그들에게 특정한 포인터를 줄 수는 없습니다. 기본 생각은 barrierAction을 사용하는 두 개의 인수로 구성된 생성자를 사용하여 장벽을 구성하는 것입니다.이 작업을 완료 한 후에 n 스레드 await() 스레드를 수행 한 후 barrierAction이 실행 된 후 n 스레드가 계속됩니다. CyclicBarrier#await()를위한 Javadoc에서

는 :

현재의 thread가 도착하는 마지막 스레드이며, null 이외의 장벽 조치를 생성자에 제공된 경우, 현재 스레드가를 허용하기 전에 작업을 실행 계속할 다른 스레드. 배리어 액션 중에 예외가 발생하면 (자), 그 예외는 현재의 thread로 전달되어 장벽은 파손 한 상태가됩니다.