[英]Multithreaded synchronized deadlock in Java
我有一个需要实时渲染的应用程序。 我有两个方法是从单独的线程访问的,这些线程访问类中的成员变量。 但是,当我尝试运行该程序时,最终我进入了两种方法都被调用的状态(即在同一元素上两次调用了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:我还应注意,这两个方法只对集合进行迭代。
两种方法都使用同一Set
实例的监视器。 因此,有一个副作用-如果一个线程在method1
,则method1
和method2
中的两个块都将锁定其他任何线程。
如果thread1进入method1
,则他进入集合监视器。 并且允许线程1在method2
进入该块(可以从method1
的块内部调用method2
)。 但是所有其他线程将不得不等待,直到线程1(最终)退出监视器。
可能需要在一个对象上同步多个块,例如,如果两个块都对集合进行了修改,并且您不想让两种方法的代码并行执行。
我希望两个线程都在Class类的同一实例上工作。
除非“ do something
”部分使用其他锁,否则您的代码不应引起任何死锁。 第二个线程可能必须等待第一个线程完成代码的同步部分,但这不是死锁:一旦第一个线程完成,第二个线程将被唤醒并完成其工作。
如果要避免争用,应该查看java.util.concurrent
包中的并发集合。
根据您的描述,不清楚第一个线程是真正的死锁还是长时间持有的锁。 如果使用这些方法进行耗时的操作,可能只是其他线程需要过多等待的副作用。
因此,您可能需要限制锁定时间间隔以避免争用。 在不知道方法内部实际发生什么的情况下,很难给出更具体的建议。 一种可能是使用并发Set实现,例如ConcurrentSkipListSet
或CopyOnWriteArraySet
。
但是,如果这不仅仅是一个长期持有的锁,请检查objects
是否已发布给外部各方。 在其他地方发布和锁定它确实可能导致死锁。
从代码示例中,根本没有涉及死锁:
第二种方法试图在第一种方法仍然拥有要锁定的对象上的锁时,这是完全正常的。
要发生死锁,必须满足以下四个条件:
由于您的代码示例中可能存在前三个,因此我将查看每个方法中是否还有其他调用正在使用另一个被锁定的资源; 或是否正在进行耗时的操作似乎造成了死锁,而实际上却没有。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.