2017-12-11 13 views
0

간략하게하려고합니다. 나의 프로젝트를 위해, 나는 기본적인 클라이언트 - 서버 프로그램을 개발하는 임무를 받았다. 클라이언트는 서버와 상호 작용하고 상호 작용하는 기본 GUI를 가지고 있으며 두 개는 중개인 (Client Manager 스레드)을 통해 상호 작용하고 서버는 Worker 스레드를 만들고 감독하는 작업자 스레드를 동적으로 만듭니다.ArrayList의 마지막 요소를 읽지 않는 다중 스레딩

클라이언트 관리자는 GUI에서 사용자의 입력을 기반으로 작업을 생성하고 서버로 전송하는 동시에 작업 관리자를 기다리고있는 사람에게 알립니다. 깨어나면 Worker Manager는 작업을 자신의 "미니언"작업자 스레드에 위임하고 작업자가 작업을 수행합니다. Worker Manager는 서버의 작업 목록에 대한 소유권을 주장하고 서버의 작업 목록에서 작업을 삭제하면서 자신을 위해 첫 번째 작업을 수행 한 다음 자신의 부하에게 제공합니다.

그러나 문제는 여러 명의 직원이 작업 목록의 전체 내용을 읽을 수 없다는 것입니다. 단일 작업자가 잘 작동하므로 다중 스레드/동시성 문제가있는 것으로 보입니다.

@Override 
public void run() { 
    try { 
     while(true) { 

      if(!server.getTaskList().isEmpty() && !minion.isWorking()) { 
       try { 
         synchronized(server.getTaskList()) { 
          while(!server.getTaskList().isEmpty() && currenttask == null) { 
          currenttask = server.getTaskList().get(0); 
          server.getTaskList().remove(0); 
          } 
         } 

         if(!minion.isAlive()) { 
          minion = new Worker(this); 
          synchronized(minion) { 
          minion.setCurrenttask(currenttask); 
          minion.start(); 
          } 
         } 
         minion.setCurrenttask(currenttask); 
         synchronized(minion) { 
          minion.setAlarmclock(true); 
          minion.notify(); 
          minion.setWorking(true); 
         } 
         synchronized(this) { 
          while(!alarmclock) 
           this.wait(); 
           alarmclock= false; 

           currenttask = null; 
         } 
        } catch (InterruptedException e) { 
         minion = new Worker(this); 
         minion.setCurrenttask(currenttask); 
         minion.start(); 
        } 
      } 
      if(server.getTaskList().isEmpty()) 
       synchronized(this) { 

        while(!alarmclock) 
         this.wait(); 
         alarmclock= false; 
        currenttask = null; 
       } 

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

그리고이 내 노동자 실행 방법 :

내 노동자 관리자의 실행 방법입니다 내 특정 인스턴스에서

@Override 
public void run() { 
    try { 
     while(true) { 

      if(currenttask != null) { 
       System.out.println(this + " new task " + currenttask); 
       if(manager.getServer().getTaskList().isEmpty()) 
        System.out.println(this + "wtf ?"); 
       work(currenttask); 
       isWorking = false; 

      } 
      else { 
       synchronized(this) { 

        while(!alarmclock) 
         this.wait(); 
         alarmclock= false; 
        } 

      } 
     } 

    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } finally { 

    } 
} 

, 나는 648 개 주어진 시간에 작업을하지만, 다수의 근로자를 /이 작업자 관리자는 640 또는 644 또는 646 만 처리 한 다음 대기하기 위해 그 중 일부를 "잊어 버린"것처럼 보입니다. 동시성 등의 경험이 없다는 것을 감안할 때 여기서 명백한 실수를했다면 지적 할 수있는 사람을 찾고 있습니다.

+0

서버의 TaskList는 어떤 종류의 개체입니까? –

+0

새로운 Minon의 출발점으로 InterruptedException을 사용하는 것에 대해 걱정하고 있습니다. 이것 좀 봐 : https://stackoverflow.com/questions/2233561/producer-consumer-workquequeues –

+0

@ LukasBradley 그것은 내가 만든 작업 객체의 arraylist입니다. 태스크는 몇 가지 속성 만 가진 매우 간단한 클래스입니다. 소유하는 ClientM과 함께 제공되는 String과 다른 기본 데이터를 더한 것입니다. WorkerM의 생성자는 이미 미니언을 생성하고 실행합니다. 인터럽트 된 경우를 대비하여 InterruptedException을 사용합니다. – TacoProgrammer

답변

0

예에서 빠진 키 코드가 있습니다 : alarmclock 및 isWorking의 초기 값은 무엇입니까? 초기 미니언은 어디에서 만들어 졌는가? 미니언의 currentTask는 작업 완료 후 null로 설정되어 있습니까?

둘째 당신이 (당신이 자신을 발견 한대로) 스레딩에 따라 정말 어렵 설계했습니다 방법은, 예를 들어 관리자에서 코드의이 비트를 가지고 :

if(!minion.isAlive()) { 
    minion = new Worker(this); 
    synchronized(minion) { 
     minion.setCurrenttask(currenttask); 
     minion.start(); 
    } 
} 
minion.setCurrenttask(currenttask); 

그것은 동일한 작업 가능 두 번 처리 할 수 ​​있습니다. 미니언 스레드가 시작될 때, 작업을 빠르게 처리하면 setCurrentTask에 대한 두 번째 호출은 미니언 스레드가 동일한 작업을 다시 시도하고 실행하게하는 것처럼 보입니다.

세 번째 문제는 몇 가지 "wtf"를 얻을 수있는 것처럼 보입니다. 출력. 예를 들어, 작업 목록에 단일 작업이있는 경우. 관리자는 작업 목록을 비울 수 있도록 작업을 제거합니다. 미니언에게이 작업을 전달할 때 작업 목록이 비어 있다고 불평 할 것입니다.

넷째, 현재 작업이 null로 설정된 미니언을 시작할 것입니다. 두 관리자 스레드는 작업 목록에 무언가가있는 경우 첫 번째 if 문을 전달할 수 있습니다. 그 중 하나는 작업을 가져오고 다른 하나는 현재 작업에 대해 null을 갖습니다. 이 null을 시간 낭비와 같은 미니언에게 보냅니다.

고치려고하는 것이 아니라 다시 쓰는 것이 좋습니다. 당신이하려고하는 것처럼 보이는 것은 작업을 대기열에서 꺼내 처리 할 스레드로 전달하는 것입니다. 나는 한 명 이상의 노동자 매니저를 가질 필요가 없다. 이것은 작업 대기열에서 읽고 다음 사용 가능한 미니언으로 작업을 전달할 수 있습니다.

다른 라이브러리를 소개하고 읽는 것을 신경 쓸 필요가 없다면 Apache Camel을 사용하면 상당히 쉽게 할 수 있습니다.당신은 this과 같은 것으로 끝날 것입니다.

Camel을 배우고 싶지 않으면 java.util.concurrent 패키지의 high level concurrency objects에 대해 알아보십시오. 예를 들어 ArrayList가 아닌 BlockingQueue을 사용합니다.

이러한 높은 개념은 특히 스레드간에 수동으로 동기화하려는 시도가 어렵고 오류가 발생하기 때문에 도입되었습니다.

관련 문제