использование tryLock () вместе с wait () и notify () / notifyAll () - PullRequest
0 голосов
/ 25 октября 2018

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

if(lock.tryLock())
{
    try
    {
        //do some actions
        lock.notifyAll(); // error throwing line
    }
    finally
    {
        lock.unlock();
    }
}

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

if(lock.tryLock())
{
    try
    {
        //do some actions

        synchronized ( lock )
        {
            lock.notifyAll(); 
        }
    }
    finally
    {
        lock.unlock();
    }
}

Моя проблема в том, что у меня есть tryLock в true, не значит ли это, что я уже получил блокировку, и я могу безопасно вызвать wait() и методы notify ()?Заранее спасибо ..

1 Ответ

0 голосов
/ 25 октября 2018

Забудьте о «гибридном подходе» здесь, это не работает.

Каждый объект имеет неявную блокировку.Это включает в себя объекты классов Lock, таких как ReentrantLock.Вызов wait и notify всегда использует неявную блокировку, эти методы не используют возможности блокировки объекта Lock, для которого вы их вызываете.Методы wait, notify и notifyAll объявлены в Object как native и final.

Чтобы получить wait и notify для работы, вам нужно синхронизировать объект блокировки, и блокировка, выполняемая такими методами, как tryLock, будетне имеет значения, это будет в конечном итоге функционально эквивалентным final Object lock = new Object();, что еще более запутанно.

Блокирующие объекты имеют свои собственные эквиваленты, если вы используете java.util.concurrent.locks.Lock, тогда получите условие из блокировки и вызовите await(что соответствует ожиданию) и signal / signalAll (эквиваленту notify / notifyAll).

С объектами блокировки вы можете иметь несколько условий, позволяющих сигнализировать подмножествам потоков, ожидающих блокировки.Как следствие, вам не нужно signalAll где-то близко, так как код с неявной блокировкой требует notifyAll.

Например, если вы посмотрите, как реализовано ArrayBlockingQueue , он использует ReentrantLock, и есть одно условие для потребителей и другое условие для производителей:

/** Main lock guarding all access */
final ReentrantLock lock;

/** Condition for waiting takes */
private final Condition notEmpty;

/** Condition for waiting puts */
private final Condition notFull;

построеноwith

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

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

/**
 * Extracts element at current take position, advances, and signals.
 * Call only when holding lock.
 */
private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length)
        takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return x;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...