3

事前設定された一連の文字列があります。アイテムを反復したいのですが、反復中に「作業を行う」必要があり、セットからアイテムを削除することもできます。各アイテムの「仕事をする」ために新しいスレッドを生成したい。「作業を行う」間、一部のアイテムのみがセットから削除されることに注意してください。

今、次の質問があります。

Collections.synchronizedSet(new HashSet()); を使用するだけでこれを達成できますか? ? 反復中にリストからアイテムを削除しているため、これにより ConcurrentModificationException がスローされると推測しています。一貫性の問題なしで上記の動作を効率的に達成するにはどうすればよいですか?

ありがとう!

4

4 に答える 4

4

ExecutorService を使用します

ExecutorService es = Executors.newFixedThreadPool(n);
List<Future<String>> toRemove = new ARraysList<>();
for(String s: set)
   toRemove.add(es.submit(new Task(s)));
for(Future<String> future : toRemove()) {
   String s = future.get();
   if (s != null)
       set.remove(s);
}

これにより、マルチスレッドでコレクションにアクセスする必要がなくなります。

于 2012-07-23T08:53:43.707 に答える
1

はい、SynchronisedSetは引き続きConcurrentModificationExceptionsをスローします。

これを試して:

Set s = Collections.newSetFromMap(new ConcurrentHashMap())

ConcurrentHashMapは、複数のスレッドがそれにアクセスして変更しているときに、ConcurrentModificationExceptionをスローしないようにする必要があります。

于 2012-07-23T09:09:18.070 に答える
1

コレクションから要素を削除し、それらをコンシューマースレッドにフィードするマスタープロデューサースレッドを使用します。コンシューマー スレッドは、アイテムを「個人的に」削除する必要はありません。

于 2012-07-23T08:51:50.607 に答える
0

アプローチは、セット内のデータと操作の正常な完了との関係によって異なります。

セットからの削除は、タスク実行の結果とは無関係です

スレッド実行の実際の結果を気にしない場合は、セットを調べて、タスクをディスパッチするときにすべてのアイテムを削除できます (すでにいくつかの例があります)。

タスクの実行が正常に完了した場合にのみセットから削除

セットからの削除が実行の成功に対するトランザクションである必要がある場合は、Futures を使用して、タスク実行の成功に関する情報を収集できます。そうすれば、正常に実行されたアイテムのみが元のセットから削除されます。Futures と anを使用して実行をチェックから分離できるため、Set 構造に同時にアクセスする必要はありませんExecutorService。例えば:

// This task will execute the job and, 
// if successful, return the string used as context
class Task implements Callable<String> {
    final String target;
    Task(String s) {
        this.target = s; 
    }
    @Override
    public String call() throws Exception {
        // do your stuff
        // throw an exception if failed 
        return target;
    }
}

そして、これはそれがどのように使用されるかです:

ExecutorService executor;

Set<Callable<String>> myTasks = new HashSet<Callable<String>>();
for(String s: set) {
   myTasks.add(new Task(s));
}
List<Future<String>> results = executor.invoqueAll(myTasks);
for (Future<String> result:results) {
    try {
        set.remove(result.get());
    } catch (ExecutionException ee) {
        // the task failed during execution - handle as required
    } catch (CancellationException ce) {
        // the task was cancelled - handle as required
    }
}
于 2012-07-23T10:13:10.917 に答える