Синхронизация Java для вызовов методов в основном из одного потока - PullRequest
1 голос
/ 31 марта 2019

У меня есть метод в классе с изменяемым состоянием, который вызывается на 99,999% из одного потока, за исключением одного раза из другого потока в ловушке завершения работы.

Вот скелет класса

public class StateHolder {
    private final Queue<String> q;

    public synchronized void add(String s) {
       this.q.offer(s);
       this.lastUpdateTime = System.currentTimeMillis();
    }

    public synchronized void removeUntil(Predicate<String> p) {
        while(!q.isEmpty()) {
           if (p.applies(q.peek()) {
                q.poll();
            } else {
                break;
            }
        }
        this.lastUpdateTime = System.currentTimeMillis();
    }

    public synchronized int pendingRecords() {
        return this.q.size();
    }

    public synchronized void shutdown(Consumer<String> c) {
        while(!q.isEmpty()) c.accept(q.poll());
    }
}

. Выше методы add, pendingRecords и removeUntil всегда будут вызываться из одного потока в течение жизниприложение (1000+ звонков в секунду в зависимости от трафика на приложение).shutdown будет вызываться другим потоком во время завершения работы приложения, которое будет происходить раз в недели.

Существует ли примитив синхронизации, который, как известно, является лучшим выбором для производительности для этого шаблона доступа?Или я должен просто использовать традиционный блок synchronized и позволить JIT разобраться?

Ответы [ 2 ]

1 голос
/ 02 апреля 2019

Я полагаю, что вы воспользуетесь преимуществом (из коробки) блокировки смещения - Смещенная блокировка в Java

0 голосов
/ 01 апреля 2019

Прежде всего, вам необходимо уточнить семантику вызовов на add, pendingRecords и removeUntil, когда происходит одновременный вызов shutdown. В текущей реализации эти вызовы будут блокироваться до тех пор, пока не вернется shutdown, но после этого они будут счастливо продолжены. Может быть, в этом случае было бы лучше бросить IllegalStateException. Но это действительно зависит от ваших требований.

Чтобы улучшить аспект параллелизма, я бы отделил проблему жизненного цикла shutdown от остальной логики приложения. Например, вы можете изменить q с Queue<String> на AtomicReference<Queue<String>>. Вызов shutdown затем установит эту ссылку на реализацию Queue, которая будет представлять происходящее завершение работы и, например, киньте IllegalStateException с на все их методы. В зависимости от точной семантики, которую вы требуете, есть еще и другие проблемы, связанные с параллелизмом, чтобы потом разобраться. Например. Это нормально для removeUntil, чтобы начать сбой в середине цикла? Если нет, то этот метод должен был бы работать из локальной копии очереди, полученной при входе в метод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...