私はクラスシリアライザーを持っています:
class Serializer<T> extends Consumer<T> {
final Consumer<? super T> actual;
// constructor omitted for brewity
@Override public synchronized void accept(T t) {
actual.accept(t);
}
}
目的は、actual が一度に 1 つのスレッドから実行されるようにすることです。ただし、ロックを保持している間にコールバックを呼び出すことは一般的に危険であるため、ロックを保持する代わりに、呼び出し元が着信値をキューに入れると、スレッドの 1 つが入り、キューを空にし、実際のコンシューマーをループで順番に呼び出します。(その他の制限は、同時呼び出し元の数がわからないことです。)
final ConcurrentLinkedQueue<T> queue;
final AtomicInteger wip;
@Override public void accept(T t) {
queue.offer(t);
if (wip.getAndIncrement() == 0) {
do {
actual.accept(queue.poll());
} while (wip.decrementAndGet() > 0);
}
}
これは機能し、無制限のキュー、スレッド ホッピング、ループ内でスタックするスレッドの問題を回避しますが、ベンチマークでは、直接メソッド呼び出しと比較して、シングル スレッドのケースで 10% のスループットが得られます。I. 同期ブロックを使用してこのキューイング/発行を実装すると、JVM が同期化を最適化しないため、ベンチマークは直接ケースの 50% を示します。これは素晴らしいことですが、スケールも大きくありません。juc.Lock を使用するとスケーリングしますが、上記のコードと同様にシングルスレッドのスループットが低下します。私が間違っていなければ、JVM が最適化されて同期化された後でも、メソッドが再び同時に呼び出されてロックが元に戻された場合に備えて、何らかのガードを使用する必要があります。
私の質問は、ロック、キュー、またはその他のシリアル化ロジックで同様の効果を達成するにはどうすればよいかということです。コードはスケーリングされ、シングル スレッドでの使用でも高速のままです。