2

여러 스레드가 스스로 등록하는 Manager 클래스가 있습니다 (요청 당 고유 식별자를 생성하기 위해 UUID 사용). 페이로드가 처리하고 해당 응답을 매니저. java.util.concurrent.ExecutorService을 사용하여 여러 스레드를 시작합니다. 여기에 여기에 Java ExecutorService : - 이벤트가 발생할 때 깨어나도록 스레드에 알립니다.

public class ManagerTest { 
    public static void main(String[] args) { 
     try { 
      Manager myManager = new Manager(); 
      // Start listening to the messages from different threads 
      myManager.consumeMessages(); 
      int num_threads = Integer.parseInt(args[0]); 
      ExecutorService executor = Executors.newFixedThreadPool(num_threads); 

      for (int i = 0; i < num_threads; i++) { 
       // class implementation is given below 
       Runnable worker = new MyRunnable(myManager); 
       executor.execute(worker); 
      } 
      executor.shutdown(); 
      // Wait until all threads are finish 
      while (!executor.isTerminated()) { 

      } 
      System.out.println("\nFinished all threads"); 

      myManager.closeConnection(); 

     } catch (IOException | TimeoutException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

많은 기능을 내 관리자를 테스트 할 수있는 구현은 MyRunnable 클래스의 구현입니다

class MyRunnable implements Runnable { 
    private Manager managerObj; 
    public MyRunnable(Manager managerObj) { 
     this.managerObj = managerObj; 
    } 

    @Override 
    public void run() {  
     try { 
      Random rand = new Random(); 
      int n = rand.nextInt(35); 
      String requestId = UUID.randomUUID().toString(); 
      managerObj.registerRequest(requestId, n); 
      managerObj.publishMessage(requestId); 
      // Want to avoid this while loop 
      while(! managerObj.getRequestStatus(requestId)){ 

      } 
      int response = managerObj.getRequestResponse(requestId); 
      // do something else 
      managerObj.unregisterRequest(requestId); 

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

요청과 페이로드에 양을 변화 걸릴 수 있습니다 요청의 응답을 따라 처리합니다 관리자 시간. 관리자가 응답을받을 때마다이 함수를 호출하여 요청 상태를 true로 설정합니다. setRequestStatus(requestId). 이 후에 스레드는 while loop에서 빠져 나와 실행을 계속합니다.

코드가 정상적으로 작동하지만 스레드가 너무 많은 작업을 수행하면 조건이 충족 될 때까지 while 루프를 반복적으로 반복해야합니다.

관리자에게 요청을 보낸 후 스레드를 잠자기 상태로 만드는 방법이며 관리자가 응답을 준비 할 때이 스레드에 신호를 보냅니다.

만약 누군가에게 너무 간단하다면, 나는 자바와 자바 스레딩 인터페이스에 초보자입니다.

+0

대신'executor.isTerminated()'의'ExecutorService.awaitTermination' 방법을 사용합니다. 자세한 것은, ExecutorService의 javadoc를 참조 해주세요. –

답변

3

우리는 간단한 질문에 익숙하며 문제는 실제로 잘 쓰여지고 풀기에 합당한 문제가 있습니다. 사람들이 원하는 것을 알지 못하고 요청하는 방법과 같이 매일 매일 많이 악화됩니다. 그 등등.

그래서, 당신이하고있는 일은 바쁜 스핀 루프입니다. 왜냐하면 a) 쓰레드 당 완전한 CPU 코어를 소비하고, b) 실제로 유지합니다. CPU가 사용 중이므로 유용한 작업이있는 다른 스레드에서 처리 시간을 훔치고 있음을 의미합니다.

이 문제를 해결할 수있는 방법은 여러 가지가 있습니다. 최악의 경우부터 최선을 다할 것입니다.

  • 코드를 개선하는 가장 간단한 방법은 매개 변수로에게 0를 전달 java.lang.Thread.sleep(long millis) 메소드를 호출하는 것입니다. 이것은 "yield"작업이라고도하며, "수행 할 유용한 작업이있는 다른 스레드가 있으면 실행하고 완료되면 다시 돌아갑니다."를 의미합니다. 이것은 100 % CPU를 소비하기 때문에 busy-spin-looping보다 약간 나은 것입니다. 이점은 다른 스레드가 수행 할 필요가없는 동안에 만 CPU를 소비하므로 적어도 다른 것들은 느려지지 않는다는 것입니다.

  • 코드를 향상시키는 데있어 가장 좋은 방법은 java.lang.Thread.sleep(long millis) 메서드를 호출하여 1을 매개 변수로 전달하는 것입니다. 이것을 패스 (pass) 연산이라고하며, 본질적으로 "타임 슬라이스의 남은 부분을 유용한 스레드가있는 다른 스레드로 릴리스"하는 것을 의미합니다. 즉, 전체 시스템에서 유용한 작업을 수행 할 필요가 없더라도 나머지 타임 슬라이스는 몰수됩니다. 이렇게하면 CPU 소비가 거의 0이됩니다. 단점은 a) CPU 소비가 실제로 제로보다 약간 상회한다는 것, b) 컴퓨터가 저전력 절전 모드로 전환 할 수 없다는 것, c) 작업자 스레드가 약간 응답 성이 떨어짐 : 타임 슬라이스 경계에서만 작업해야합니다.이 답변에 설명 된대로

  • 문제를 해결하는 가장 좋은 방법은, 자바에 내장 된 동기화 메커니즘을 사용하는 것입니다 : https://stackoverflow.com/a/5999146/773113이 단지 제로 CPU를 소비하지 않습니다,하지만 그것도 기계가 들어갈 수 있습니다 저전력 모드.

  • 일반적인 상황에 대한 문제를 해결하려면 조건을 기다리지 않고 실제로 수행 할 작업에 대한 정보를 전달하려면 BlockingQueue을 사용하십시오. (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html) 블로킹 큐 은 하나 개의 스레드가 다른 정보를 전달할 수 있도록 내장 동기화 메커니즘을 사용하여 자바 작동한다.

+0

액터 사용 및/또는 반응 방식 (CompletableFuture)으로 프로그래밍 패러다임 전환에 대해 언급 할 수 있습니다. 어쨌든 좋은 답변. @GPI 덕분에 – GPI

+0

. 나는 다른 프로그래밍 패러다임을 사용하라는 제안은 기존의'while()'루프에서 일부 코드를 채워서 문제를 해결할 가능성이있을 때 약간의 과잉이라고 생각한다. ('BlockingQueue'에 대한 제안은 이미 지나치게 과장되어 있다고 생각합니다.) 그러나 교육의 이익을 위해, 그것은 상처를주지 않을 것입니다. 문제는, 그것에 대해 편안한 글쓰기를 느끼기에 충분한 반응이 없다는 것입니다. 그러니 자유롭게 해주세요. –

0

이미 @MikeNakis의 훌륭한 답변이 있지만 다른 옵션도 제공하고 싶습니다.

이 옵션은 그러나 미래를 반환 할 관리자 API를 변경하는 것을 포함한다.

  • 장소에서 이러한 변경 MyRunnable의 변경할 수 있습니다 run() 방법 getRequestResponse() 반환 Future<Integer>

을 만드는 getRequestStatus() 방법을 드롭 : 내가 제안 것이다 관리자

변경이 있습니다 :

public void run() {  
    try { 
     Random rand = new Random(); 
     int n = rand.nextInt(35); 
     String requestId = UUID.randomUUID().toString(); 
     managerObj.registerRequest(requestId, n); 
     managerObj.publishMessage(requestId); 

     // Future.get() blocks and waits for the result without consuming CPU 
     int response = managerObj.getRequestResponse(requestId).get(); 
     // do something else 
     managerObj.unregisterRequest(requestId); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (InterruptedException e) { 
     Thread.currentThread().interrupt(); 
    } 
} 

Future<> 가능성이 자바의 java.util.concurrent.FutureTask을 사용하는 것입니다 구현하는 가장 쉬운 방법.

관련 문제