Безопасно ли синхронизировать блок без использования ключевого слова wait? - PullRequest
2 голосов
/ 26 июля 2011

У меня есть один поток, заполняющий векторный объект значениями, и еще один поток, который периодически извлекает значения из него и периодически очищает его.

Я хочу, чтобы один поток обращался к вектору, а другой - к паузе. Нужно ли использовать ключевые слова wait / notify / notifyAll?

private Vector<Long> _recordIdsSent;

# Called by main thread (before thread A and thread B are started)
public ConstructorOfThisClass() {
    _recordIdsSent = new Vector<Long>();
    // Starts thread A & B
}

# Called by thread A
public void addSentRecordIds( List<Long> ids ) {
    synchronized (_recordIdsSent) {
        _recordIdsSent.addAll( ids );
    }
}

# Called by thread B
public void deleteRecords()
{
    List<Long> ids;
    synchronized (_recordIdsSent) {
        ids = (List<Long>) _recordIdsSent.clone();
        _recordIdsSent.clear();
    }

    // Delete the records matching ids....
}

Примечание. Я клонирую вектор _recordIdsSent, поскольку операция удаления может занять некоторое время.

[EDIT] Перемещено синхронизированное ключевое слово из сигнатуры метода в переменную _recordIdsSent

Ответы [ 5 ]

4 голосов
/ 26 июля 2011

Вам не нужно.Просто поместите синхронизированный блок в addSentRecordIds, как deleteRecords.Таким образом, вы будете получать доступ к вашему вектору только по одной ветке за раз.

1 голос
/ 27 июля 2011

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

Вы можете использовать пул из одного потока (java.util.concurrent):

Executor executor = Executors.newSingleThreadExecutor();

и когда вы хотите записать / удалить в БД:

executor.call(new Runnable() {
   @Override public void run() { ... write to DB}
});

executor.call(new Runnable() {
   @Override public void run() { ... delete in DB}
});

Где бы вы их ни называли, они будут работать в одном потоке.

РЕДАКТИРОВАТЬ:из javadoc newSingleThreadExecutor: «Создает исполнителя, который использует один рабочий поток, работающий в неограниченной очереди. (Обратите внимание, однако, что если этот единственный поток завершается из-за сбоя во время выполнения до завершения работы, то новый при необходимости занимает его местовыполнять последующие задачи.) Задачи гарантированно выполняются последовательно, и в любой момент времени будет активна не более одной задачи. В отличие от эквивалентного в противном случае newFixedThreadPool (1) возвращаемый исполнитель гарантированно не может быть реконфигурируем для использования дополнительных потоков.

1 голос
/ 26 июля 2011

Как уже отмечалось, в предоставленном коде доступ к вектору из addSentRecordIds не синхронизирован - это делает ваш код небезопасным

Кроме того, этот фрагмент кода не гарантирует, что объект, используемый дляблокировка (_recordIdsSent) не меняется - это делает ее небезопасной .Сам я обычно предпочитаю выделенные объекты для блокировки, потому что это делает мои намерения чище и менее подвержено ошибкам.Как это:

private final Object lock = new Object(); // dedicated lock object
//... _recordIdsSent, addSentRecordIds etc...
public void deleteRecords()
{
    List<Long> ids;
    synchronized (lock) {
        ids = (List<Long>) _recordIdsSent.clone();
        _recordIdsSent.clear();
   }

   // Delete the records matching ids....
}
0 голосов
/ 26 июля 2011

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

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

0 голосов
/ 26 июля 2011

Вам не нужно это делать, поскольку синхронизированное ключевое слово делает это за вас.

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

...