2016-10-13 2 views
0

マルチスレッド環境での割り当てリストを維持する必要があります。 Executorサービスはサーバースレッドを数分間実行し、 各スレッドは共有リストから割り当てを追加または削除できます。共有リストでのマルチスレッドの処理

2つ以上のスレッドが同じ割り当てを削除しようとしている状況を防ぐ必要があります。

私は、どのコレクションを使用するか、どのように使用するかを決定するためにtrubleを持っています。 同期化されたブロックの中にリストをラップすることなくそれを行う方法はありますか?

私が読んだところから、CopyOnWritearraylistは主に読み込みに適しています。 Collections.synchronizedListは答えですか?

ありがとうございました。

public class AssignmentsListComponent { 

    private List<Assignment> assignmentsList; 
    private boolean isClosed; 

    public AssignmentsListComponent(List<Assignment> assignmentsList) { 
     super(); 
     this.assignmentsList = assignmentsList; 
     this.isClosed = false; 
    } 

    public void addAssignment(Assignment assignment) throws ClosedAssignmentsListException{ 
     if(this.isClosed) 
      throw new ClosedAssignmentsListException("Cannot add Assignment. List is closed"); 
     this.assignmentsList.add(assignment); 
    } 

    public void removeAssignment()throws ClosedAssignmentsListException{ 
     if(this.isClosed) 
      throw new ClosedAssignmentsListException("Cannot add Assignment. List is closed"); 
     Assignment assignmentToRemove = this.assignmentsList.get(new Random().nextInt(this.assignmentsList.size()-1)); 
     this.assignmentsList.remove(assignmentToRemove); 
     this.isClosed = this.assignmentsList.isEmpty(); 
    } 


} 


public class SchedulerService { 

    private final static long PERIOD = 1500;//Repeat interval 
    private final static long TIME_TO_RUN = 5; 

    private Map <Family, AssignmentsListComponent> familyAssignmentsListMap; 

    private void initializeFamilyAssignmentsListMap(){ 
     List<Family> families = HibernateUtil.getAllFamilies(); 
     for(Family family :families){ 
      List<Assignment> assignmentsList = new ArrayList<Assignment>(); 
      AssignmentsListComponent assignmentsListComponent = new AssignmentsListComponent(assignmentsList); 
      this.familyAssignmentsListMap.put(family, assignmentsListComponent); 
     } 

    } 

    public void runFamilyMemberThreds(){ 

     List<Assignment> allAssignments = HibernateUtil.getAllAssignments(); 
     List<FamilyMember> familyMembers = HibernateUtil.getAllFamilyMembers(); 
     // create executor to run the threads for all family members 
     ScheduledExecutorService executorService = Executors.newScheduledThreadPool(familyMembers.size()); 

     /** 
     * For each of the family members, run in PERIOD intervals, and kill it's task after TIME_TO_RUN in minutes 
     */ 
     for(FamilyMember familyMember :familyMembers){ 
      // Execute task every 30 seconds 
      final ScheduledFuture<?> handler = executorService.scheduleAtFixedRate(new FamilyMemberThred(familyMember, allAssignments, familyAssignmentsListMap.get(familyMember.getFamily())),0,PERIOD,TimeUnit.MILLISECONDS); 
      Runnable cancel = new Runnable() 
      { 
       public void run() 
       { 
        handler.cancel(true); 
       } 
      }; 
      executorService.schedule(cancel,TIME_TO_RUN,TimeUnit.MINUTES);//Cancels the task after 5 minutes 
      executorService.shutdown(); 
     } 
    } 
} 


public class FamilyMemberThred implements Runnable{ 

     private List<Assignment> allAssignments; 
     private FamilyMember familyMember; 
     private AssignmentsListComponent assignmentsListComponent; 

     public void run() { 
      if(isAddAssignment()) 
       addAssignment(); 
      else 
       removeAssignment(); 
     } 

     private void addAssignment(){ 
      Assignment randomAssignmentToAdd = this.allAssignments.get(new Random().nextInt(allAssignments.size()-1)); 
      try { 
       this.assignmentsListComponent.addAssignment(randomAssignmentToAdd); 
      } catch (Exception e) { 
       logClosedListException(randomAssignmentToAdd); 
      } 
     } 

     private void removeAssignment(){ 
      try { 
       this.assignmentsListComponent.removeAssignment(); 
      } catch (Exception e) { 
       logClosedListException(null); 
      } 
     } 

     private void logClosedListException(Assignment assignment){ 
      Log log = new Log(familyMember, Action.ADD, assignment, Boolean.FALSE); 
      HibernateUtil.saveLog(log); 
     } 


     /** 
     * possible values between 0-2, so if 1 or 2, return true 
     */ 
     private boolean isAddAssignment(){ 
      return new Random().nextInt(3) > 0; 
     } 

    } 
+0

実際にはget(index)のようなListメソッドが必要ですか?そうでない場合、これはBlockingQueueの完璧なユースケースのように思えます。 – yshavit

+0

私はしませんが、なぜ私は割り当ての数に制限がなく、リストがクリアされたら、私はそれを閉じ、次の追加を待つことはないので、完璧なユースケースです。 – user3845295

+0

呼び出し 'put'に値を入れているスレッド、それらを取り出すスレッドは' take'を呼び出し、同期は実装によって処理されます。 (put/take以外の方法がありますが、セマンティクスが異なります。https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/BlockingQueue.htmlを参照してください) – yshavit

答えて

0

おそらく、同期するための最も簡単な方法は、AssignmentsListComponentクラスの各メソッドのため同期キーワードを追加することです。 これはすべてのメソッドを同期させます。つまり、その実行は排他的に変更可能です。

AssignmentsListComponentのassignmentsListフィールドは、コンストラクタにパラメータとして渡されます。 呼び出し元がリストを変更する可能性があります(このコードスニペットではできませんが、これを行うことができます)。最後の問題のため

考えられる解決策:

  • コンストラクタの内部で空のリストを作成し、フィールドに割り当てます。もちろん、呼び出し側はリストをコンストラクタに渡すことはできません。それはおそらく大丈夫です。そうでない場合は...
  • パラメータをコピーし、そのコピーをフィールドassignmentsListに割り当てます。
+0

すべてのアイデアは、各家族が独自のリストを持っているということです。もし私が各メンバーのリストのコピーを作るなら、それは共有リストではないでしょう。 – user3845295

+0

AssignmentsListComponentインスタンスをスレッド間で共有します。ここでリストを共有する必要はありません。 AssignmentsListComponentインスタンスは、異なるスレッドからのアクセスを同期します。 この実装では、AssignmentsListComponentクラスの各パブリックメソッドに添付されている** synchronize **キーワードが「マジック」を行います。 – Slava

0

CopyOnWriteArrayListは、非効率的であるため挿入がたくさんあることを計画していないと良いです。別のスレッドが反復処理をしている間に、削除操作が主に懸念されるようです。別の投稿からは、並行クラスが優先されます。あなたはここで読むことができます:Difference between CopyOnWriteArrayList and synchronizedList

関連する問題