2013-06-12 2 views
1

Как решить голод с notify и notifyall?Как решить голод с уведомлением и notifyall?

Если у нас есть 4 потоков, ожидающих получить блокировку на тот же OBJ, и текущий поток вызывает notify()

Виртуальная машина Java будет выбрать какую-то одну нить. Возможно ли, что поток, который называется notify(), снова может быть выбран JVM, так как после вызова notify() он будет также в списке ожидающих потоков.

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

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

Кроме того, такие же проблемы возникли бы с notifyall() Я думаю, где мы не знаем, какой поток будет выбран.

ответ

3

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

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

+0

Приложение, основанное на справедливости для корректности, плохо спроектировано. Но приложение может также полагаться на справедливость (в среднем) минимизировать время выполнения. –

3

В принципе, нити случайным образом выбираются из функции notify() или notifiAll(). Что вы можете сделать, так это то, что вы можете использовать ReetrantLock со Справедливой политикой. Справедливая политика избегает голодания.

private final ReentrantLock lock = new ReentrantLock(true); 
+1

ReentrantLock имеет [новое условие] (http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantLock.html#newCondition%28%29), чтобы вы могли подождать/уведомлять семантику с честной блокировкой. Это условие справедливо, 'notify' запускает потоки в порядке FIFO. –

+0

@ DavidSchwartz. В точку. – lvarayut

1

Это не легко решить с ожиданием/уведомит (все), но вы можете посмотреть на ReentrantLock/Состояние справедливостью значение ИСТИНА. ReentrantLock javadoc. Не имеет значения, какой из ваших потоков получает работу.

0

Нет, поток, вызывающий Object.notify должен владеть замок на объекте и уведомит потоки, которые ранее relinqueshed их замки на этом объекте с помощью вызова Object.wait - уведомляющее поток не может Simultaniously уведомления и ждать.

От Object.notify JavaDoc:

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

1

Давайте спросим, ​​что фактически делает виртуальная машина. Я посмотрю OpenJDK.После некоторого копания (исходный код here) мы обнаруживаем, что метод wait реализован этим битом C++.

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)) 
    JVMWrapper("JVM_MonitorWait"); 
    Handle obj(THREAD, JNIHandles::resolve_non_null(handle)); 
    assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object"); 
    JavaThreadInObjectWaitState jtiows(thread, ms != 0); 
    if (JvmtiExport::should_post_monitor_wait()) { 
    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms); 
    } 
    ObjectSynchronizer::wait(obj, ms, CHECK); 
JVM_END 

Что мы следуем вниз следующие определенные для ObjectSynchronizer

void ObjectSynchronizer::waitUninterruptibly (Handle obj, jlong millis, TRAPS) { 
    if (UseBiasedLocking) { 
    BiasedLocking::revoke_and_rebias(obj, false, THREAD); 
    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); 
    } 
    if (millis < 0) { 
    TEVENT (wait - throw IAX) ; 
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative"); 
    } 
    ObjectSynchronizer::inflate(THREAD, obj()) -> wait(millis, false, THREAD) ; 
} 

, а затем в ObjectMonitor возвращенного inflate, где мы находим этот воздаем

// Enter the waiting queue, which is a circular doubly linked list in this case 
    // but it could be a priority queue or any data structure. 

и некоторые ObjectWaiter код, реализующий вышеупомянутый двусвязный список.

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

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

Смежные вопросы