簡體   English   中英

在本地資源上同步時Java死鎖?

[英]Java Deadlock during a synchronized on a local resource?

我看到同一行代碼中多個線程死鎖的問題。 我不能在本地或任何測試中重現問題,但是生產中的線程轉儲已經非常清楚地顯示了問題。

我無法理解為什么線程會在下面的同步線上被阻塞,因為在調用堆棧或任何其他線程中的Object上沒有其他同步。 有沒有人知道發生了什么,或者我怎么能重現這個問題(目前嘗試15個線程都在循環中擊中trim(),同時通過我的隊列處理2000個任務 - 但無法重現)

在下面的線程轉儲中,我認為具有“鎖定”狀態的多個線程可能是Java Bug的一種表現形式: http ://bugs.java.com/view_bug.do?bug_id = 8047816其中JStack報告線程處於錯誤狀態。 (我使用的是JDK版本:1.7.0_51)

干杯!

這是一個線程轉儲中的線程視圖.....

"xxx>Job Read-3" daemon prio=10 tid=0x00002aca001a6800 nid=0x6a3b waiting for monitor entry [0x0000000052ec4000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.mycompany.collections.CustomQueue.remove(CustomQueue.java:101)
    - locked <0x00002aae6465a650> (a java.util.ArrayDeque)
    at com.mycompany.collections.CustomQueue.trim(CustomQueue.java:318)
    at com.mycompany.collections.CustomQueue.itemProcessed(CustomQueue.java:302)
    at com.mycompany.collections.CustomQueue.trackCompleted(CustomQueue.java:147)
    at java.util.concurrent.ThreadPoolExecutor.afterExecute(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
    - <0x00002aaf5f9c2680> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"xxx>Job Read-2" daemon prio=10 tid=0x00002aca001a5000 nid=0x6a3a waiting for monitor entry [0x0000000052d83000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.mycompany.collections.CustomQueue.remove(CustomQueue.java:101)
    -  locked <0x00002aae6465a650> (a java.util.ArrayDeque)
    at com.mycompany.collections.CustomQueue.trim(CustomQueue.java:318)
    at com.mycompany.collections.CustomQueue.itemProcessed(CustomQueue.java:302)
    at com.mycompany.collections.CustomQueue.trackCompleted(CustomQueue.java:147)
    at java.util.concurrent.ThreadPoolExecutor.afterExecute(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
    - <0x00002aaf5f9ed518> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"xxx>Job Read-1" daemon prio=10 tid=0x00002aca00183000 nid=0x6a39 waiting for monitor entry [0x0000000052c42000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.mycompany.collections.CustomQueue.remove(CustomQueue.java:101)
    - waiting to lock <0x00002aae6465a650> (a java.util.ArrayDeque)
    at com.mycompany.collections.CustomQueue.trim(CustomQueue.java:318)
    at com.mycompany.collections.CustomQueue.itemProcessed(CustomQueue.java:302)
    at com.mycompany.collections.CustomQueue.trackCompleted(CustomQueue.java:147)
    at java.util.concurrent.ThreadPoolExecutor.afterExecute(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
    - <0x00002aaf5f9ecde8> (a java.util.concurrent.ThreadPoolExecutor$Worker)


"xxx>Job Read-0" daemon prio=10 tid=0x0000000006a83000 nid=0x6a36 waiting for monitor entry [0x000000005287f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.mycompany.collections.CustomQueue.remove(CustomQueue.java:101)
    - waiting to lock <0x00002aae6465a650> (a java.util.ArrayDeque)
    at com.mycompany.collections.CustomQueue.trim(CustomQueue.java:318)
    at com.mycompany.collections.CustomQueue.itemProcessed(CustomQueue.java:302)
    at com.mycompany.collections.CustomQueue.trackCompleted(CustomQueue.java:147)
    at java.util.concurrent.ThreadPoolExecutor.afterExecute(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

這是提取的Java代碼,它顯示了錯誤的位置......

public class Deadlock {
        final Deque<Object> delegate  = new ArrayDeque<>();
        final long maxSize = Long.MAX_VALUE;

        private final AtomicLong totalExec = new AtomicLong();
        private final Map<Object, AtomicLong> totals = new HashMap<>();
        private final Map<Object, Deque<Long>> execTimes = new HashMap<>();

        public void trim() {
            //Possible optimization is evicting in chunks, segmenting by arrival time
            while (this.totalExec.longValue() > this.maxSize) {
                final Object t = this.delegate.peek();
                final Deque<Long> execTime = this.execTimes.get(t);
                final Long exec = execTime.peek();
                if (exec != null && this.totalExec.longValue() - exec > this.maxSize) {
                    //If Job Started Inside of Window, remove and re-loop
                    remove();
                }
                else {
                    //Otherwise exit the loop
                    break;
                }
            }
        }

        public Object remove() {
            Object removed;
            synchronized (this.delegate) { //4 Threads deadlocking on this line !
                removed = this.delegate.pollFirst();
            }
            if (removed != null) {
                itemRemoved(removed);
            }
            return removed;
        }

        public void itemRemoved(final Object t) {
            //Decrement Total & Queue
            final AtomicLong catTotal = this.totals.get(t);
            if (catTotal != null) {
                if (!this.execTimes.get(t).isEmpty()) {
                    final Long exec = this.execTimes.get(t).pollFirst();
                    if (exec != null) {
                        catTotal.addAndGet(-exec);
                        this.totalExec.addAndGet(-exec);
                    }
                }
            }
        }
    }

HashMap文檔

請注意,此實現不同步。 如果多個線程同時訪問哈希映射,並且至少有一個線程在結構上修改了映射,則必須在外部進行同步。

(強調他們的)

您正在以不同步的方式讀取和寫入Map

我認為沒有理由認為您的代碼是線程安全的。

我建議你在一個無限循環trim造成這種缺乏線程安全的。

輸入同步塊相對較慢,因此線程轉儲可能總是至少顯示等待獲取鎖的幾個線程。

你的第一個線程是在等待pollFirst持有鎖。

"xxx>Job Read-3" daemon prio=10 tid=0x00002aca001a6800 nid=0x6a3b waiting for monitor entry [0x0000000052ec4000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.mycompany.collections.CustomQueue.remove(CustomQueue.java:101)
    - locked <0x00002aae6465a650> (a java.util.ArrayDeque)
    at com.mycompany.collections.CustomQueue.trim(CustomQueue.java:318)

其他線程正在等待獲取鎖定。 您將需要提供整個線程轉儲以確定哪個線程持有0x0000000052ec4000上的鎖,這是阻止您的pollFirst調用返回的原因。

為了死鎖,你需要至少兩個同時鎖定同一個線程中的至少兩個對象,這是你發布的代碼似乎沒有做的事情。 你指出的錯誤可能適用,但是當我讀到它時,這是一個美化問題並且線程沒有被“鎖定”,而是等待獲取對有問題的對象(ArrayDeque)的鎖定。 如果遇到死鎖,您應該在日志中看到“死鎖”消息。 它會調出兩個相互阻塞的線程。

我不相信線程轉儲說有死鎖。 它只是告訴你在轉儲時有多少線程在監視器上等待。 由於在給定時刻只有一個線程可能有監視器,因此不應該非常令人驚訝。

您在應用程序中看到哪些行為導致您認為自己陷入僵局? 你的代碼中有很多缺失,特別是委托Dequeue中的對象來自哪里。 我的猜測是你沒有徹頭徹尾的僵局,但其他一些問題可能看起來像死鎖。

感謝此處的回復,很明顯問題是沒有線程安全使用多個集合。

為了解決這個問題,我使trim方法同步並用LinkedBlockingDeque(Concurrent Collections FTW!)替換HashMap與ConcurrentHashMap和ArrayDeque的使用。

進一步計划的增強是將2個單獨的Maps的使用更改為包含自定義對象的單個Map,這樣可以使操作(在itemRemoved中)保持原子狀態。

暫無
暫無

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

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