2010-07-30 3 views
6

특히,이 코드 조각에 어떤 문제가 있는지 말해 줄 수 있습니까? 스레드를 시작해야하므로 "Entering thread .."를 5 번 인쇄 한 다음 notifyAll()이 호출 될 때까지 기다려야합니다. 하지만, 그것은 "Entering .."과 "Done .."을 무작위로 인쇄하고 다른 사람들을 기다리고 있습니다.여러 스레드에서 wait 및 notify 프로토콜을 사용하는 방법

public class ThreadTest implements Runnable { 
    private int num; 
    private static Object obj = new Object(); 
    ThreadTest(int n) { 
     num=n; 
    } 
    @Override 
    public void run() { 
     synchronized (obj) { 
      try { 
       System.out.println("Entering thread "+num); 
       obj.wait(); 
       System.out.println("Done Thread "+num); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static void main(String[] args) { 
     Runnable tc; 
     Thread t; 
     for(int i=0;i<5;i++) { 
      tc = new ThreadTest(i); 
      t = new Thread(tc); 
      t.start(); 
     } 
     synchronized (obj) { 
      obj.notifyAll(); 
     } 
    } 
} 

답변

8

당신은 메소드 호출에 노골적으로 잘못 아무것도하지 않는,하지만 당신은 race condition 있습니다.

모든 작업자 스레드가 wait() 호출에 도달 한 후 주 스레드가 동기화 된 블록에 도달하더라도 (사용자가 명시 적으로 가상 시스템에 원하지 않는 주 스레드와 순서대로 스레드를 만들어서 실행할 스레드). 스레드 스케줄러가 모든 작업자 스레드를 즉시 차단하기로 결정한 즉시 (예 : 코어가 하나 인 경우) 주 스레드가 계속 진행될 수 있습니다. 캐시 실패로 인해 작업자 스레드가 컨텍스트 전환 된 것일 수 있습니다. 한 작업자 스레드가 I/O (인쇄 문)를 차단하고 주 스레드가 그 자리에서 전환 될 수 있습니다.

따라서 모든 작업자 스레드가 wait() 호출에 도달하기 전에 주 스레드가 동기화 된 블록에 도달하면 wait() 호출에 도달하지 않은 작업자 스레드는 의도 한대로 작동하지 않습니다. 현재 설정으로는 이것을 제어 할 수 없으므로이를 명시 적으로 처리해야합니다. 각 작업자 스레드가 wait()에 도달 할 때마다 증가하는 일종의 변수를 추가하거나이 변수가 5에 도달 할 때까지 주 스레드가 notifyAll()을 호출하지 않도록하거나 주 스레드 루프를 가지고 notifyAll()을 반복적으로 호출 할 수 있습니다. 작업자 스레드는 여러 그룹에서 해제됩니다.

java.util.concurrent 패키지를 살펴보십시오 - 기본 동기화 된 잠금보다 더 강력한 기능을 제공하는 여러 가지 잠금 클래스가 있습니다. Java가 바퀴를 다시 발명하지 못하도록합니다. CountDownLatch은 특히 적합 할 것 같습니다.

요약하면 동시성은 입니다. 스레드가 원치 않는 주문과 원하는 주문에서 실행될 때 모든 것이 여전히 작동하도록 설계해야합니다.

+0

감사합니다. 나는 여기에 간단한 해결 방법은 모든 스레드가 자신의 wait() 상태가되었는지 확인하기 위해 notifyAll() 전에 Thread.sleep (1000)을 넣는 것입니다. – figaro

+0

잘 작동 할 수도 있지만 순진한 해결책입니다. 대기 호출 전에 더 많은 작업을 수행하려는 경우 가능한 한 빨리 작업자 스레드를 해제해야하거나 작업자 스레드가 예상보다 오래 걸리는 경우에도 동일한 문제가 발생합니다. 잠금을 사용해야합니다. 그것이 바로 동시 프로그래밍이 전부입니다. David Blevin의 코드를 살펴보십시오. 이는 카운트 다운 잠금을 사용하는 좋은 예입니다. – Scott

0

코드와 루트 문제는 스레드의 일부는 메인 스레드가 notifyAll를 호출 후까지 wait에 도착하지 않는다는 것입니다. 따라서 이 (가)이 될 때까지 아무 것도 일어나지 않습니다.

대기/알림을 사용하여이 작업을 수행하려면 모든 하위 스레드가 해당 호출을하기 전에 알림을받을 수있는 상태가 될 때까지 대기 할 수 있도록 주 스레드를 동기화해야합니다.

일반적으로 wait, notify 및 기본 잠금과의 동기화는 까다로운 작업입니다. 대부분의 경우 Java 동시성 유틸리티 클래스를 사용하면 더 나은 결과 (더 간단하고 안정적이며 효율적인 코드)를 얻을 수 있습니다.

2

두 번째로 CountDownLatch 권장 사항입니다. multithreadedness 또는 가능한 한에 가까운 도전 하나는

final int threadCount = 200; 

final CountDownLatch startPistol = new CountDownLatch(1); 
final CountDownLatch startingLine = new CountDownLatch(threadCount); 
final CountDownLatch finishingLine = new CountDownLatch(threadCount); 

// Do a business method... 
Runnable r = new Runnable() { 
    public void run() { 
     startingLine.countDown(); 
     try { 
      startPistol.await(); 

      // TODO: challenge the multithreadedness here 

     } catch (InterruptedException e) { 
      Thread.interrupted(); 
     } 
     finishingLine.countDown(); 
    } 
}; 

// -- READY -- 

for (int i = 0; i < threadCount; i++) { 
    Thread t = new Thread(r); 
    t.start(); 
} 

// Wait for the beans to reach the finish line 
startingLine.await(1000, TimeUnit.MILLISECONDS); 

// -- SET -- 

// TODO Assert no one has started yet 

// -- GO -- 

startPistol.countDown(); // go 

assertTrue(finishingLine.await(5000, TimeUnit.MILLISECONDS)); 

// -- DONE -- 

// TODO: final assert 

아이디어는 당신의 쓰레드가 실행 코드의 바로 다음 라인을 보장 할 수 있습니다 : 여기 내 멀티 스레드 테스트에 사용하는 패턴이다. 나는 트랙에서 주자로 스레드를 생각합니다.(start/create/start), 시작 라인에 완벽하게 정렬 될 때까지 기다린다. (모든 사람들은 startingLine.countDown()을 호출했다.) 그리고 시작 권총 (startPistol.countDown())을 시작하여 모든 사람을 기다린다. 결승선을 건너십시오 (finishingLine.countDown()).

[편집] startingLine.await()과 startingPistol.countDown() 사이에서 실행하려는 코드 나 검사가없는 경우 startingLine과 startingPistol을 하나의 순환 바리어로 결합 할 수 있습니다 (threadCount +1). 이중 CountDownLatch 접근법은 사실상 동일하며 다른 모든 스레드가 정렬 된 후 실행이 시작되기 전에 주 테스트 스레드가 모든 설정/검사를 수행 할 수 있습니다.

관련 문제