надлежащее использование энергозависимого ключевого слова

Я думаю, что у меня есть довольно хорошая идея о volatile ключевое слово в Java, но я думаю о рефакторинге некоторого кода, и я думал, что это будет хорошая идея использовать его.

у меня есть класс, который является в основном рабочим как Кэш DB. это содержит набор объектов, которые это считало из базы данных, запросов подач на те объекты, и затем иногда обновляет базу данных (на основе тайм-аута). Вот скелет

public class Cache
{
    private HashMap mappings =....;
    private long last_update_time;
    private void loadMappingsFromDB()
    {
        //....
    }
    private void checkLoad()
    {
        if(System.currentTimeMillis() - last_update_time > TIMEOUT)
            loadMappingsFromDB();
    }
    public Data get(ID id)
    {
        checkLoad();
        //.. look it up
    }
}

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

Затем я думал, почему бы не использовать volatile

я мог определить ссылку карты как энергозависимую

private volatile HashMap mappings =....;

и затем в get (или где-либо еще который использует переменную отображений), я просто сделал бы локальную копию ссылки:

public Data get(ID id)
{
    HashMap local = mappings;
    //.. look it up using local
}

и затем фоновый поток просто загрузился бы во временную таблицу и затем подкачал бы ссылки в классе

HashMap tmp;
//load tmp from DB
mappings = tmp;//swap variables forcing write barrier

Этот подход имеет смысл? и действительно ли это на самом деле ориентировано на многопотоковое исполнение?

1
задан luke 10 June 2010 в 16:23
поделиться

4 ответа

В существующих ответах на этот вопрос есть некоторая дезинформация. Использование volatile на самом деле является хорошим шагом в обеспечении безопасности потоков. См. пункт 3 в Развенчание мифов о языке программирования Java Питера Хаггара из IBM. Хаггар приводит небольшую предысторию и пример, но суть заключается в следующем:

Итак, как атомарные операции могут быть не потокобезопасными? Основной момент заключается в том, что они действительно могут быть потокобезопасными, но нет никакой гарантии, что это так. Потокам Java разрешено хранить частные копии переменных отдельно от основной памяти.

Используя volatile, вы гарантируете, что потоки обращаются к основной памяти и не используют частные копии переменных, о которых вы не знаете или не ожидаете.

Отвечая на ваш вопрос: да, ваша стратегия безопасна.

EDIT:
В ответ на другое сообщение, вот раздел JLS о volatile полях.

2
ответ дан 2 September 2019 в 23:51
поделиться

Имеет ли смысл такой подход? и действительно ли это потокобезопасно?

Это имеет смысл и является потокобезопасным. Во всяком случае, до некоторой степени. Некоторые вещи, о которых следует подумать:

  • При обновлении вы позволяете приложению читать старые устаревшие значения. Это то, что ты задумал? Это нормально для некоторых приложений, в других случаях вы можете захотеть заблокировать, пока кеш не будет обновлен ( FutureTask упрощает такое поведение).
  • Когда срабатывает loadMappingsFromDB () , поток, первоначально вызвавший get (ID) , блокируется до завершения обновления.
  • Несколько потоков могут вызывать checkLoad () одновременно, что означает, что если перезагрузка идет медленно и у вас есть несколько потоков, вызывающих get (ID) , вы можете получить массу одновременные обновления. Хотя результат будет таким же, это будет пустой тратой системных ресурсов. Легкий способ исправить это в вашем текущем коде - иметь AtomicBoolean , который вы проверяете перед обновлением:

     private final AtomicBoolean isUpdating = new AtomicBoolean (false);
    приватная недействительная checkLoad ()
    {
    если (System.currentTimeMillis () - last_update_time <= TIMEOUT) return;
    если (! isUpdating.compareAndSet (false, true)) return; // уже обновляется
    пытаться {
    loadMappingsFromDB ();
     } наконец {
    isUpdating.set (ложь);
     }
    }
    
1
ответ дан 2 September 2019 в 23:51
поделиться

На самом деле, предложенный вами подход имеет смысл даже без использования ключевого слова 'volatile'. Поскольку присваивание ссылок (mappings = tmp;) является атомарной операцией (переводится как одна машинная команда), нет никакой вероятности несогласованности памяти:
http://java.sun.com/docs/books/tutorial/essential/concurrency/atomic.html
Чтение и запись атомарны для ссылочных переменных и для большинства примитивных переменных (все типы, кроме long и double).

Идея ключевого слова 'volatile' заключается в том, что если вы измените такую переменную в момент времени T, то любой другой поток, обращающийся к ней в момент времени T + x (x > 0), увидит новое значение. В противном случае, даже после изменения значения остается некоторый промежуток времени, в течение которого другие потоки могут видеть старое значение. Но это, похоже, не является проблемой для вас.
edit
Та же идея описана в ссылке выше.

-1
ответ дан 2 September 2019 в 23:51
поделиться

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

Вы могли бы так же легко получить метод get (id) и часть loadMappingsFromDB () , которая перезаписывает ссылку (не всю загрузку из БД, а только перезапись). -присвоение отображений ) синхронизировать на одной и той же блокировке.

Честно говоря, я бы рассмотрел возможность повторного использования установленной библиотеки кэширования, такой как EhCache, которая имеет функции для фоновой загрузки или загрузки при запуске, поскольку эти библиотеки, вероятно, уже давно решили все проблемы с синхронизацией, и вы можете вернитесь к беспокойству о логике вашего приложения, а не о низкоуровневой безопасности домашнего кеша.

0
ответ дан 2 September 2019 в 23:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: