Java中的多线程同步死锁

时间:2011-02-22 10:48:40

标签: java multithreading synchronization deadlock

我有一个需要实时渲染的应用程序。我有两个方法从访问类中的成员变量的单独线程访问。但是,当我尝试运行程序时,最终我进入一个状态,其中两个方法都被调用(即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 个答案:

答案 0 :(得分:2)

上面这样的代码本身永远不会死锁,因为只有一个同步对象,即“对象”。

如果thread2被阻止,那么仅仅因为thread1还没有完成method1 / method2。

当它被阻止时,只需打印线程转储并进行分析(或在此处发布?)。

如果method1或method2中有其他同步因素,那么你需要提一下。

编辑:在你的线程转储中,我可以看到线程实际上在awtLock上被阻止了。这看起来像threading issue of Swing

答案 1 :(得分:1)

两种方法都使用相同Set实例的监视器。因此,作为副作用 - 如果一个线程在method1中工作,则method1method2中的两个块都将被锁定以用于任何其他线程。

如果thread1进入method1,则进入集监视器。并且允许thread1进入method2中的块(method2可以从method1中的块内部调用{{1}}。但是所有其他线程都必须等到thread1(最终)退出监视器。

可能需要在一个对象上同步多个块,就好像两个块都对集合进行了修改,并且您不希望两个方法的代码并行执行。

答案 2 :(得分:0)

我希望两个线程都在同一个Class类的实例上工作。

答案 3 :(得分:0)

除非&#34; do something&#34;部分使用其他锁,您的代码不应导致任何死锁。第二个线程可能必须等待第一个线程完成代码的synchronized部分,但这不是死锁:一旦第一个线程完成,第二个线程将被唤醒并完成其工作。

如果您想避免争用,您应该查看java.util.concurrent包中的并发集合。

答案 4 :(得分:0)

从您的描述中,您不清楚第一个线程是否存在真正的死锁或长时间锁定。如果您在这些方法中执行耗时的操作,则可能只是其他线程需要过度等待的副作用。

因此,您可能需要限制锁定间隔以避免争用。在不知道方法内部实际发生了什么的情况下,很难给出更具体的建议。一种可能性是使用并发Set实现,例如ConcurrentSkipListSetCopyOnWriteArraySet

如果它不仅仅是长时间锁定,请检查objects是否发布给外部各方。在其他地方发布和锁定它可能确实会导致死锁。

答案 5 :(得分:0)

从代码示例中,根本没有涉及死锁:
第二种方法在第一种方法仍然存在的情况下尝试锁定对象上的锁定是完全正常的。

要发生死锁,必须遵循4个条件:

  • 相互排斥 [与对象一样 - 只有一个可以一次持有]
  • 等待 [您可以在抓住其他人时请求新锁]
  • 没有先发制人 [你不能强行锁定
  • 最后,循环等待 [资源1正在等待资源2释放锁定,资源2正在等待资源 n 释放锁定,资源< em> n 正在等待资源1释放其锁定]

由于前3个可能存在于您的代码示例中,我将看看是否在每个方法中还有进一步调用正在使用另一个具有锁定的资源;或者是否有耗时的操作似乎会造成僵局,而事实上它们并非如此。