Если библиотека классов Java не предоставляет подходящего инструмента синхронизации, вам необходимо создать собственный инструмент синхронизации.
Структура для блокировки операций, зависящих от состояния
Скопируйте код кода следующим образом:
получить блокировку состояния объекта;//Запрос на получение блокировки
while(предварительное условие не выполняется){//Предварительное условие не выполнено
отпустить блокировку;//сначала отпустить блокировку
дождаться, пока предварительное условие может выполняться;//подождать, пока предварительное условие не будет выполнено
опционально сбой, если прервано или истекло время ожидания; //Выполнение не удалось из-за прерывания или тайм-аута
повторно получить блокировку;//Повторить попытку получения блокировки
}
выполнить действие // выполнить
снятие блокировки;//снятие блокировки
Пример базового класса реализации ограниченного кэша
Скопируйте код кода следующим образом:
общественный класс BaseBoundBuffer<V> {
частный финал V[] buf;
частный int хвост;
частная голова int;
частный счетчик int;
@SuppressWarnings («не отмечено»)
public BaseBoundBuffer (int емкость) {
buf = (V[]) новый объект[емкость];
}
публичная синхронизированная недействительность doPut (V v) {
буф[хвост] = v;
если (++хвост == buf.length)
хвост = 0;
считать++;
}
общедоступный синхронизированный V doTake() {
V v = buf[голова];
если (++head == buf.length)
голова = 0;
считать--;
вернуть v;
}
публичный окончательный синхронизированный логический isFull() {
счетчик возврата == buf.length;
}
публичный окончательный синхронизированный логический isEmpty() {
счетчик возврата == 0;
}
}
Способ реализации блокировки 1: выдать вызывающему объекту исключение
Скопируйте код кода следующим образом:
общедоступная синхронизированная пустота put1 (V v) выдает исключение {
если (isFull())
выдать новое исключение («полная ошибка»);
доПут (в);
}
Анализ: исключения следует использовать при возникновении исключений. Здесь нецелесообразно создавать исключения; вызывающая сторона должна обрабатывать ситуацию, когда предварительное условие не выполняется, что не решает фундаментальную проблему.
Способ реализации блокировки 2: через опрос и сон
Скопируйте код кода следующим образом:
public void put2(V v) выдает InterruptedException {
while (true) {//Опрос
синхронизировано (это) {
если (!isFull()) {
доПут (в);
возвращаться;
}
}
Thread.sleep(SLEEP_TIME);//Сон
}
}
Анализ: сложно оценить настройку времени сна SLEEP_TIME. Если параметр слишком мал, ЦП может опрашивать несколько раз, потребляя больше ресурсов ЦП. Если параметр слишком велик, скорость реагирования будет ниже;
Третий метод реализации блокировки: условная очередь
Элементы в очереди условий представляют собой потоки, ожидающие выполнения связанных условий один за другим. Каждый объект Java можно использовать в качестве блокировки, и каждый объект также можно использовать в качестве очереди условий, а методы wait, notify и notifyAll в Object составляют API внутренней очереди условий. Object.wait автоматически снимет блокировку и запросит операционную систему приостановить текущий поток, чтобы другие потоки могли получить блокировку и изменить состояние объекта. Object.notify и Object.notifyAll могут разбудить ожидающий поток, выбрать поток из очереди условий для пробуждения и попытаться повторно получить блокировку.
Скопируйте код кода следующим образом:
общедоступная синхронизированная пустота put3 (V v) выдает InterruptedException {
в то время как (isFull())
ждать();
допут (v);
уведомитьВсе();
}
Анализ: получите лучший ответ, простой и удобный в использовании.
Используйте условную очередь
1. Условный предикат
1).Определение: Условный предикат является предварительным условием для того, чтобы операция стала операцией, зависящей от состояния. Условный предикат — это выражение, состоящее из отдельных переменных состояния в классе. Например, условный предикат для метода put — «кеш не пуст».
2).Отношения. В условном ожидании существует важная троичная связь, включая блокировку, метод ожидания и условный предикат. В условный предикат включено несколько переменных состояния, и каждая переменная состояния должна быть защищена блокировкой, поэтому перед проверкой условного предиката необходимо удерживать блокировку. Объект блокировки и объект условной очереди (а также объект, в котором вызываются методы ожидания и уведомления) должны быть одним и тем же объектом.
3). Ограничения: каждый вызов ожидания будет неявно связан с определенным предикатом условия. При вызове определенного предиката условия вызывающая сторона уже должна удерживать блокировку, связанную с очередью условий. Эта блокировка также должна защищать предикат условия компонента. переменные состояния
2. Правила использования условной очереди
1). Обычно имеется условное сказуемое.
2). Всегда проверяйте условные предикаты перед вызовом ожидания и тестируйте снова после возврата из ожидания;
3). Всегда вызывайте ожидание в цикле;
4) Убедитесь, что переменные состояния, составляющие предикат условия, защищены блокировкой, и эта блокировка должна быть связана с очередью условий;
5) При вызове wait, notify и notifyAll должна удерживаться блокировка, связанная с очередью условий;
6) После проверки условного предиката не снимать блокировку до начала выполнения защищенной логики;
3.Уведомление
Попробуйте использовать notifyAll вместо nofify, потому что nofify случайным образом переводит поток из состояния ожидания в состояние «Заблокировано» (состояние «Заблокировано» — это поток, который всегда пытается получить блокировку, то есть, как только он обнаруживает, что блокировка доступна, он немедленно удерживает блокировку), а notifyAll пробуждает все потоки в очереди условий из состояния ожидания в состояние блокировки. Рассмотрим такую ситуацию, если поток A переходит в состояние ожидания из-за предиката условия Pa, а поток B. переходит в состояние покоя из-за предиката состояния Pb. В это время Pb. истинно, поток C выполняет одно уведомление. Если JVM случайно выбирает поток A для пробуждения, то поток A проверяет, что условный предикат Pa не истинен, и затем переходит в состояние сна. С этого момента ни один другой поток не может быть пробуждён. , и программа всегда будет в спящем режиме, если вы используете. notifyAll отличается. JVM переведет все ожидающие потоки в очереди условий из состояния сна в состояние Blocked. Даже если поток выбран случайным образом и переходит в состояние сна, поскольку предикат условия неверен, другие потоки будут конкурировать за блокировку и блокировку. продолжить выполнение.
4. Стандартный код копии метода зависимости состояния выглядит следующим образом:
void stateDependentMethod throwsInterruptedException {
синхронизировано (блокировка) {
пока(!условиепредикат))
блокировка.ожидание();
}
//сделаем что-нибудь();
....
уведомитьВсе();
}
Показать объект условия
Явный объект Condition является более гибкой альтернативой и обеспечивает более широкие функциональные возможности: для каждой блокировки может существовать несколько ожиданий, условные ожидания могут быть прерываемыми или непрерывными, ожидания на основе времени, а также справедливые или несправедливые операции с очередью. Условие может быть связано с блокировкой точно так же, как очередь условий связана со встроенной блокировкой. Чтобы создать условие, вызовите метод Lock.newCondition для связанного замка. Следующий код используется для повторной реализации ограниченного кэша с использованием переменных условия отображения. Код выглядит следующим образом:
общественный класс ConditionBoundedBuffer<V> {
частный финал V[] buf;
частный int хвост;
частная голова int;
частный счетчик int;
приватная блокировка блокировки = новый ReentrantLock();
частное условие notFullCondition = lock.newCondition();
частное условие notEmptyCondition = lock.newCondition();
@SuppressWarnings («не отмечено»)
public ConditionBoundedBuffer (int емкость) {
buf = (V[]) новый объект[емкость];
}
public void doPut(V v) выдает InterruptedException {
пытаться {
блокировка.блокировка();
while (count == buf. length)
notFullCondition.await();
буф[хвост] = v;
если (++хвост == buf.length)
хвост = 0;
считать++;
notEmptyCondition.signal();
} окончательно {
блокировка.разблокировка();
}
}
public V doTake() выдает InterruptedException {
пытаться {
блокировка.блокировка();
пока (счет == 0)
notEmptyCondition.await();
V v = buf[голова];
буф[голова] = ноль;
если (++head == buf.length)
голова = 0;
считать--;
notFullCondition.сигнал();
вернуть v;
} окончательно {
блокировка.разблокировка();
}
}
}