簡體   English   中英

Java中的多線程同步死鎖

[英]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:我還應注意,這兩個方法只對集合進行迭代。

上面的代碼本身不會死鎖,因為只有一個同步對象,即“ 對象 ”。

如果線程2被阻塞,則僅僅是因為線程1尚未完成method1 / method2。

當其阻塞時,只需打印線程轉儲並進行分析(或在此處發布?)。

如果method1或method2中還有其他同步因素,則需要提及。

編輯 :在您的線程轉儲,我可以看到該線程實際上在awtLock上被阻止。 這看起來像是Swing線程問題

兩種方法都使用同一Set實例的監視器。 因此,有一個副作用-如果一個線程在method1 ,則method1method2中的兩個塊都將鎖定其他任何線程。

如果thread1進入method1 ,則他進入集合監視器。 並且允許線程1在method2進入該塊(可以從method1的塊內部調用method2 )。 但是所有其他線程將不得不等待,直到線程1(最終)退出監視器。

可能需要在一個對象上同步多個塊,例如,如果兩個塊都對集合進行了修改,並且您不想讓兩種方法的代碼並行執行。

我希望兩個線程都在Class類的同一實例上工作。

除非“ do something ”部分使用其他鎖,否則您的代碼不應引起任何死鎖。 第二個線程可能必須等待第一個線程完成代碼的同步部分,但這不是死鎖:一旦第一個線程完成,第二個線程將被喚醒並完成其工作。

如果要避免爭用,應該查看java.util.concurrent包中的並發集合。

根據您的描述,不清楚第一個線程是真正的死鎖還是長時間持有的鎖。 如果使用這些方法進行耗時的操作,可能只是其他線程需要過多等待的副作用。

因此,您可能需要限制鎖定時間間隔以避免爭用。 在不知道方法內部實際發生什么的情況下,很難給出更具體的建議。 一種可能是使用並發Set實現,例如ConcurrentSkipListSetCopyOnWriteArraySet

但是,如果這不僅僅是一個長期持有的鎖,請檢查objects是否已發布給外部各方。 在其他地方發布和鎖定它確實可能導致死鎖。

從代碼示例中,根本沒有涉及死鎖:
第二種方法試圖在第一種方法仍然擁有要鎖定的對象上的鎖時,這是完全正常的。

要發生死鎖,必須滿足以下四個條件:

  • 互斥 [就像處理對象一樣-一次只能容納一個對象]
  • 按住並等待 [您可以在按住別人的同時請求新的鎖]
  • 不搶先 [您不能強行加鎖
  • 最后, 循環等待 [資源1等待資源2釋放鎖,資源2等待資源n釋放鎖,資源n等待資源1釋放鎖]

由於您的代碼示例中可能存在前三個,因此我將查看每個方法中是否還有其他調用正在使用另一個被鎖定的資源; 或是否正在進行耗時的操作似乎造成了死鎖,而實際上卻沒有。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM