2014-08-19 3 views
0

У меня есть приложение с одним потоком писателя, который делает некоторые вещи и обновляет некоторые показатели, например. счетчики и т. д. В приложении есть несколько других потоков, которые читают статистику и делают с ними что-то похожее. Не обязательно, чтобы показатели были в актуальном состоянии, но поток писателя должен быть заблокирован как можно меньше времени. Мне было интересно, какие из следующих бы лучше удовлетворить мои потребности:AtomicXXX lazySet для одиночной записи писателя

Вариант 1 - иметь не атомное поле, только автор может обновлять, и поле AtomicXXX, которое устанавливается с помощью lazySet:

class Stats1 
{ 
    private final AtomicLong someCounterAtomic = new AtomicLong(0); 
    private long someCounter = 0; 

    // writer thread updates the stats using this 
    public void incrementCounter(long counterIncrement) 
    { 
    someCounter += counterIncrement; 
    someCounterAtomic.lazySet(someCounter); 
    } 

    // reader threads call this 
    public long getCounterValue() 
    { 
    return someCounterAtomic.get(); 
    } 
} 

вариант 2 - Просто поле AtomicXXX, который обновляется с помощью addAndGet:

class Stats2 
{ 
    private final AtomicLong someCounter = new AtomicLong(0); 

    // writer thread updates the stats using this 
    public void incrementCounter(long counterIncrement) 
    { 
    someCounter.addAndGet(counterIncrement); 
    } 

    // reader threads call this 
    public long getCounterValue() 
    { 
    return someCounter.get(); 
    } 
} 

Или я был бы лучше делать что-то еще?

Заранее спасибо.

ответ

1

В случае немного последние статистика хорошая, я ничего не синхронизирую. Я хотел бы, чтобы поток писателя принимал периодические снимки и периодически публиковал их (или после того, как статистика изменилась) через атомную ссылку.

public class Stats { 
    // the currently available stat instance 
    public static AtomicReference<Stats> CURRENT_STATS = 
     new AtomicReference<>(new Stats()); 

    private final int whatever1; 
    private final int whatever2; 

    private Stats() { 
     this(0, 0); 
    } 

    public Stats(int whatever1, int whatever2) { 
     this.whatever1 = whatever1; 
     ... 
    } 

    public int getWhatever1() { 
     return whatever1; 
    } 

    public int getWhatever2() { 
     return whatever2; 
    } 

} 

Писатель просто создает новый экземпляр Stats, когда он считает нужным, и устанавливает ссылку CURRENT_STATS. Читатели просто читают ссылку, когда им нужна статистика, и сохраняйте ссылку до тех пор, пока они не будут выполнены с текущим переходом обработки (избегают изменения статистики при обработке).

Для этого требуется минимальная синхронизация, подходящая, когда требования к обновленной статистике являются снисходительными (например, для отображения пропускной способности и т. Д. Для пользователя). Независимо от того, сколько статистических данных предоставляет, одна атомная ссылка может контролировать все из них.

+0

Я принял этот ответ, потому что мне нужно сохранить буферизацию/задержку обрабатываемых данных потоком, записывающим статистику до минимума. Спасибо за предложение. – PaddyD

0

В среде с одним потоком вы можете использовать lazySet. getAndSet в основном для многопоточной среды

+0

, но увеличивает неатомное значение и использует это, чтобы установить атомное значение с помощью lazySet быстрее, чем с помощью addAndGet? – PaddyD

+0

@PaddyD есть. Эффективная производительность лучше, если вы используете один поток. –

3

Просто используйте addAndGet. Это проще, мгновенно узнаваемо и, вероятно, достаточно быстро.

Если есть только один поток, обновляющий счетчик, то addAndGet не оспаривается и будет примерно так же быстро, как ваш код Stats1. Если обновление нескольких потоков, то Stats1 просто неверно. В любом случае, вы не должны усложнять код, если у вас нет бенчмаркинга/профилирования данных, что говорит о том, что это значительная стоимость общего времени выполнения вашего приложения.

1

Если вы используете Java 8, вы должны рассмотреть LongAdder класс в java.util.concurrent.atomic:

Этот класс, как правило, предпочтительнее AtomicLong, когда несколько потоков обновить общую сумму, которая используется для таких как сбор статистики, а не для мелкозернистого управления синхронизацией. В условиях низкого уровня обновления два класса имеют схожие характеристики. Но в условиях высокой конкуренции ожидаемая пропускная способность этого класса значительно выше, за счет более высокого потребления пространства.

Обратите внимание, что если вы будете следовать предложению в принятом ответе никогда не синхронизации, то возможно, что статистика не будет никогда обновлять (в зависимости от того, что еще происходят в вашей программе, архитектура которой вы работаете на, какая версия Java и т. д.).

+0

К сожалению, по разным причинам я не могу использовать Java 8 для этого приложения. Этот класс выглядит так, как будто он подходит. – PaddyD