ReadWriteLocks-(не реентерабельный), как он поддерживает несколько читателей для получения блокировки чтения

Я встречал ниже не повторяющуюся реализацию ReadWriteLock во многих руководствах.

public class ReadWriteLock{

    private int readers       = 0;
    private int writers       = 0;
    private int writeRequests = 0;

    public synchronized void lockRead() throws InterruptedException{
      while(writers > 0 || writeRequests > 0){
        wait();
      }
    readers++;
    }

    public synchronized void unlockRead(){
      readers--;
      notifyAll();
    }

    public synchronized void lockWrite() throws InterruptedException{
      writeRequests++;

      while(readers > 0 || writers > 0){
        wait();
      }
      writeRequests--;
      writers++;
    }

    public synchronized void unlockWrite() throws InterruptedException{
      writers--;
      notifyAll();
    }
}

Вопрос:

Объект (скажем lock) этого класса является общим для всех потоков чтения и записи для синхронизации.

Предположим, что Reader T1 вызывает lock.lockRead(), это получает блокировку на объекте блокировки, и Reader T2 одновременно вызывает lockRead() на том же объекте. Но T1 уже заблокировал объект, поэтому T2 должен быть заблокирован и ожидает в очереди.

Итак, как код позволяет нескольким читателям одновременно устанавливать readLock?

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

1 ответ

Это правда, что никакие 2 потока не могут одновременно выполнять тело lockRead() метод. Но это не обязательно для правильной работы шаблона чтения / записи с ожидаемой производительностью.

Важно то, что lockRead() метод быстро возвращается, если нет активных авторов (wait не вызывается). Блокировка снимается, когда метод заканчивается, что позволяет другому потоку также получить блокировку чтения.

Так что, да, акт получения блокировки чтения (увеличение readers) сериализуется. Но это происходит так быстро, что работает просто отлично.

Проиллюстрировать:

private ReadWriteLock lock = new ReadWriteLock(); // this instance is shared by all threads

public void ReadSomething() {
    try {
        lock.lockRead(); // serialized, but very quick

        PerformWork(); // potentially slower, but is concurrent
    } finally {
        lock.unlockRead(); // serialized, but very quick
    }
}

Если 2 потока пытаются запустить выше ReadSomething() метод в то же время, это правда, что только один поток сможет выполнить lock.lockRead() вовремя. Но как только этот метод вернется к одному из потоков, второй поток сможет выполнить его также. И призыв к lock.lockRead() происходит так быстро, что вы даже не сможете заметить, что один поток ожидал другого.

Важно то, что оба потока могут выполнять больше времени PerformWork() в то же время.

Другие вопросы по тегам