Многопоточный синхронизированный тупик в Java - PullRequest
0 голосов
/ 22 февраля 2011

У меня есть приложение, которое требует рендеринга в реальном времени.У меня есть два метода доступа из отдельных потоков, которые обращаются к переменной члена в классе.Однако, когда я пытаюсь запустить программу, в конце концов я попадаю в состояние, когда оба метода были вызваны (т.е. synchronized был вызван дважды для одного и того же элемента), и второй поток блокируется, ожидая, что первый поток снимет блокировку напредмет.Ниже приведен пример кода:

public class Class {
private final Set<Object> objects;

...

public void method1() {
    synchronized(objects) {
        // do something
    }
}

public void method2() {
    synchronized(objects) {
        // do something else
    }
}
}

Это неверно?Как правильно выполнить эти операции, не вызывая взаимоблокировку?Спасибо

РЕДАКТИРОВАТЬ: Вот трассировка стека от JConsole

Name: Thread-6432
State: BLOCKED on java.util.HashSet@25a6cc45 owned by: AWT-EventQueue-0
Total blocked: 1  Total waited: 0

Stack trace: 
com.sonogenics.renderer.renderElements(Elements.java:81)
com.sonogenics.renderer.CameraHandler$Setup.run(CameraHandler.java:106)
java.lang.Thread.run(Thread.java:619)


Name: AWT-EventQueue-0
State: WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@656c50af owned by: pool-1-thread-1
Total blocked: 11,051  Total waited: 11,232

Stack trace: 
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842)
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178)
java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
sun.awt.SunToolkit.awtLock(SunToolkit.java:236)
sun.java2d.pipe.RenderQueue.lock(RenderQueue.java:94)
sun.java2d.pipe.BufferedRenderPipe$AAParallelogramPipe.fillParallelogram(BufferedRenderPipe.java:443)
sun.java2d.pipe.PixelToParallelogramConverter.drawGeneralLine(PixelToParallelogramConverter.java:264)
sun.java2d.pipe.PixelToParallelogramConverter.drawLine(PixelToParallelogramConverter.java:62)
sun.java2d.pipe.ValidatePipe.drawLine(ValidatePipe.java:44)
sun.java2d.SunGraphics2D.drawLine(SunGraphics2D.java:2098)
com.sonogenicsArrow.preview(Arrow.java:97)
com.sonogenics.Elements.previewElements(Elements.java:116)
   - locked java.util.HashSet@25a6cc45
com.sonogenics.PreviewOverlay.render(PreviewOverlay.java:49)
com.sonogenics.VideoPanel.renderJava2DOverlays(VideoPanel.java:89)
   - locked java.util.ArrayList@22542822
com.sonogenics.VideoPanel.paintComponent(VideoPanel.java:64)
javax.swing.JComponent.paint(JComponent.java:1029)
com.sonogenics.VideoPanel.paint(VideoPanel.java:53)
javax.swing.JComponent.paintChildren(JComponent.java:862)
   - locked java.awt.Component$AWTTreeLock@4316e1c9
javax.swing.JComponent.paint(JComponent.java:1038)
javax.swing.JComponent.paintChildren(JComponent.java:862)
   - locked java.awt.Component$AWTTreeLock@4316e1c9
javax.swing.JComponent.paint(JComponent.java:1038)
javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
javax.swing.JComponent.paintChildren(JComponent.java:862)
   - locked java.awt.Component$AWTTreeLock@4316e1c9
javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1479)
javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1410)
javax.swing.RepaintManager.paint(RepaintManager.java:1224)
javax.swing.JComponent.paint(JComponent.java:1015)
java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
java.awt.Container.paint(Container.java:1780)
java.awt.Window.paint(Window.java:3375)
com.sonogenics.Demonstrator.paint(nDemonstrator.java:234)
javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
com.sonogenics.RepaintManager.paintDirtyRegions(RepaintManager.java:64)
javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

РЕДАКТИРОВАТЬ 2: Я должен также отметить, что эти два метода ничего не делают, но итерируют через набор.

Ответы [ 6 ]

2 голосов
/ 22 февраля 2011

Вышеприведенный код как таковой сам по себе никогда не заблокируется, потому что существует только один объект синхронизации, т. Е. « objects ».

Если thread2 заблокирован, то это просто потому, что thread1 не завершил method1/method2.

Когда он заблокирован, просто распечатайте дамп потока и проанализируйте (или опубликуйте здесь?).

Если в method1 или method2 есть какой-то другой фактор синхронизации, то вам нужно упомянутьчто.

EDIT : В вашем дампе потока я вижу, что поток фактически заблокирован на awtLock.Это похоже на проблему в Swing .

1 голос
/ 22 февраля 2011

Оба метода используют монитор одного и того же экземпляра Set. Таким образом, как побочный эффект - если один поток работает в method1, оба блока в method1 и method2 заблокированы для любого другого потока.

Если поток1 вводит method1, он входит в монитор наборов. И поток1 может войти в блок в method2 (method2 может быть вызван изнутри блока в method1). Но все остальные потоки должны будут ждать, пока thread1 (наконец) не выйдет из монитора.

Может потребоваться синхронизация нескольких блоков в одном объекте, например, если оба блока вносят изменения в набор, и вы не хотите, чтобы код из обоих методов выполнялся параллельно.

0 голосов
/ 22 февраля 2011

Из примера кода вообще не существует тупиков:
Совершенно нормально, что второй метод пытается получить блокировку объекта, который блокируется, пока первый метод все еще имеет его.

Для возникновения тупика должны применяться 4 условия:

  • Взаимное исключение [как у вас с объектом - только один может удерживать его одновременно]
  • удерживайте и ждите [вы можете запросить новые блокировки, удерживая других]
  • без прерывания [вы не можете принудительно захватить блокировку
  • и наконец, Циклическое ожидание [ресурс 1 ожидает, пока ресурс 2 снимет блокировку, ресурс 2 ожидает, что ресурс n снимет блокировку, а ресурс n ожидает ресурса 1, чтобы снять блокировку]

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

0 голосов
/ 22 февраля 2011

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

Поэтому вам может потребоваться ограничить интервалы блокировки, чтобы избежать конфликтов.Не зная, что на самом деле происходит внутри методов, трудно дать более конкретный совет.Одной из возможностей может быть использование параллельной реализации Set, такой как ConcurrentSkipListSet или CopyOnWriteArraySet.

Если это не просто длительная блокировка, проверьтепубликуется ли objects сторонним организациям.Публикация и блокировка на нем где-то еще может действительно привести к тупику.

0 голосов
/ 22 февраля 2011

Если в разделах "do something" не используются другие блокировки, ваш код не должен вызывать тупиковых ситуаций.Второму потоку может потребоваться дождаться, пока первый завершит синхронизированный раздел кода, но это не тупик: как только первый поток завершится, второй поток будет разбужен и завершит свою работу.

Если вы хотите избежать конкуренции, вам следует взглянуть на одновременные коллекции в пакете java.util.concurrent.

0 голосов
/ 22 февраля 2011

Я надеюсь, что оба потока работают на одном экземпляре класса Class.

...