| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java: Общие вопросы > Программа с несколькими потоками и семафорами |
| Автор: libertas 9.4.2014, 08:55 | ||
| Всем привет! Написал программу, где есть несколько потоков - читатели и писатели. Есть воображаемая БД. Когда читатель хочет что- нибудь прочитать, он проверяет не пишет ли писатель сейчас данные в БД. Если пишет - то читатель ожидает. Аналогично писатель - ожидает пока все читатели прочитают. Читателей может быть несколько одновременно, писатель - только один. Нужно задачку решить через семафоры, которые нужно написать самому.
Я написал семафор - Semaphore, который в зависимости от значения value проверяет - читать / писать. Я понимаю, что эти семафоры не атомарны, но задачку надо решить именно через семафоры, написанные вручную. В этом коде проиходит исключение: Exception in thread "Thread-3" java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at Database.startRead(MyProg.java:113) at Reader.run(MyProg.java:39) В чем может быть дело? Спасибо |
| Автор: LSD 9.4.2014, 19:03 |
| Ошибка говорит о том, что метод wait() должен вызываться потоком которых захватил монитор на объект (у которого вызыватся wait()). Но это все не важно, тут есть проблемы серьезней: 1. Непонятно причем тут семафоры, когда это типичный read-write lock. 2. Реализовать семафор самостоятельно без "нативной" поддержки нельзя. Или надо использовать synchronized или готовую реализацию из java.util.concurrent. |
| Автор: Mirkes 9.4.2014, 22:33 |
Пожалуй рискну не согласиться. Реализовать семафор все-таки можно, но семафор нужно опрашивать на предмет разрешенности действия. А вот опроса семафора я в коде не увидел. По идее должно быть что-то вроде: Читатель семафору "Можно читать?", Семафор читателю "Можно", тогда стартует чтение. Правда при многопоточности нет гарантии, что между ответом семафора и собственно началом чтения не влезет кто-то еще. Для предотвращения такого конфликта, семафор в случае положительного ответа сначала блокирует доступ другим, а уже затем выдает ответ. Получается кривовато, но работоспособно, если читатель не забудет читать после запроса и т.д. Но в предположении приличного поведения читателей и писателей семафор сделать можно. В реальное, не учебное, приложение я бы такой семафор не включал |
| Автор: LSD 10.4.2014, 10:49 | ||
1. Не решается вопрос как "усыпить" поток на время ожидания захвата семафора. Можно конечно busy spin сделать, но это не очень хорошее решение в общем случае. 2. Без блокировок или CAS нельзя произвести атомарный инкремент/декремент (хватит ли одного CAS для реализации семафора я не уверен). Твой семафор работает неправильно. В случае если счетчик на нуле, и 2 потока вызовут acquire(), счетчик станет -2. Потом один поток вызовет release() счетчик станет -1 и один из потоков пробудится, что неправильно. |
| Автор: libertas 10.4.2014, 11:06 | ||
Извините, можно пояснить. Где/когда 2 моих потока будут вызывать acquire()? И где/когда один мой поток будет вызывать release()? |
| Автор: LSD 10.4.2014, 14:03 | ||||
У тебя там куча ошибок: 1. Операция ++ не атомарна. В многопоточной среде может давать неправильный результат. Плюс у тебя переменные не volatille потоки увидят изменения сделанные друг другом в непредсказуемый момент. 2. Операция
не атомарна. Возможна ситуация: - проверили условие оно истинно - ОС усыпила поток - другой поток поменял что-то и условие уже не выполняется - ОС разбудила первый поток и он пошел выполнять doOperation(), хотя условие уже не выполняется 3. Логические ошибки в Database у тебя в начале метода startRead() вызывается sem.P(), который поддерживает только один поток. Т.е. второй читатель будет ждать, хотя читатели должны работать паралельно. 4. Реализация самого Semaphore: - поток 1 заходит в startRead() и вызывает sem.P() и усыпляется ОС где-то в середине метода, значение value = 0 - поток 2 заходит в startRead() и вызывает sem.P() и засыпает, значение value = -1 - поток 3 заходит в startRead() и вызывает sem.P() и засыпает, значение value = -2 - поток 1 выходит из метода и вызывает sem.V(), просыпается один из потоков 2 или 3, значение value = -1 В принципе котракт не нарушен - только один поток выполняется, но странно. |
| Автор: libertas 10.4.2014, 14:32 | ||||||||
Если Вы имеете в виду : readerCount++; то она защищена семафором. Если поток заснул, то другой все равно не сможет её изменить. А если про эту: ++value; то она находится в методе synchronized - следовательно может меняться только одним потоком. Так работает synchronized .
Это невозможно, потому что if (<check condition>) doOperation(); защищено synchronized семафором.
Тоже не может быть, потому что sem.P() synchronized . Есть там, конечно не точность в коде. Сейчас только увидел:
В этом участке, если читатель пройдет семафор и уснет, то другие читатель не смогут заходить в этот блок, пока не проснется этот читатель и не откроет семафор. |
| Автор: LSD 10.4.2014, 16:13 |
| Замечание насчет vollatile все равно остается в силе. Все замечания про защиту монитром верны до тех пор пока у него count = 1, ты его используешь как обычный мьютекс. Проще уж сразу было делать synchronized и не мучаться. Речь не про sem.P(), а про вызывающий метод. |
| Автор: Mirkes 10.4.2014, 20:14 | ||
Хорошее решение должно опираться на синхронизацию или конкуренцию (со второй не знаком пока). Я же написал, что в приличную программу я бы предложенный мной семафор не включил. "Усыпление" потока осуществляется тупо - он просится, пока не получит разрешения. Это очень расточительно по ресурсам, но осуществимо. Довольно давно под DOS мне приходилось реализовывать многозадачность. Это куча проблем, жестоко карается любая ошибка, но в принципе осуществимо. Единственное, что мне не понятно, зачем давать такое как учебную задачу. |
| Автор: libertas 11.4.2014, 07:48 | ||||
да, про volatile нужно почитать. Ну да тут мьютекс получается. Согласен, что проще делать быть через synchronized, просто задание было сделать именно через семафоры. Добавлено через 37 секунд
Ну да, ресурсы цпу жрет - крутится в цикле. |
| Автор: LSD 14.4.2014, 12:48 | ||||
Это и есть busy spin, более того он иногда оправдае, например java.util.concurrent.atomic его используют. Основная проблема во втором пункте:
Я немного посмотрел - CAS хватит для реализации семафора с busy spin. |