簡體   English   中英

Java - 線程卡在“停放”狀態

[英]Java - Thread stuck in “Park” status

我無法同時運行 100 多個線程。 當我進行線程轉儲時,我注意到其中許多都處於parked status ,即

停車等待 <0x00000000827e1760> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)。

該程序在大約 25 個或更少線程的情況下運行良好。 有沒有辦法確定導致並發鎖的原因,和/或防止它? 這是使用 Executor 服務在 200 的固定池大小中運行的。

為缺少代碼道歉 - 它是專有的,有很多東西需要改變來混淆它。

您所指的類 (ConditionObject) 用於鎖定對象不被多個線程同時訪問。 Javadoc沒有描述您提到的線程狀態,但這是我的猜測:

您鎖定的對象被一個線程阻塞了很長時間,以至於其他線程開始堆積在鎖上。 一旦持有鎖的線程釋放它,下一個線程就會繼續獲取鎖。 在那個新線程完成他的工作之前,新線程會堆積在鎖后面。

如果我的猜測是正確的,那么可以:

  • 減少每個線程在鎖中花費的時間,或
  • 將線程分配到不同的鎖定對象上(如果您的問題允許),或
  • 您使用不需要鎖定的實現。

在不知道您的問題域的情況下,我希望以上信息足以為您指明可能對您有所幫助的方向。

您是否在使用某種ThreadPoolExecutor,例如java.util.concurrent.Executors類提供的那些? 也許您正面臨着任務被靜默未捕獲的異常完成的情況。 轉儲片段看起來像一個不活動的池線程,獲得不活動線程(應該是活動的)的一個原因是拋出異常但被默認線程池實現包圍。

LockSupport.park()

在線程池中,等待任務的線程被LockSupport.park();鎖定LockSupport.park(); . 請參閱來自 openjdk 的java.util.concurrent.locks.AbstractQueuedSynchronizer 源代碼

public final void await() throws InterruptedException {
    // code omitted
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // code omitted
}

這意味着線程正在執行的任務已完成(是否突然),現在線程正在等待另一個任務執行(請參閱java.util.concurrent.ThreadPoolExecutor openjdk source ):

private Runnable getTask() {
    // ...
    Runnable r = timed ?
        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
        workQueue.take();  <== the thread is blocked here  
    // ...
}

可以看到,線程在調用workQueue.take();被鎖定了workQueue.take(); .

因此,很快,處於“停放狀態”的線程只是在前一個任務完成后等待新任務。

為什么我的任務不再運行?

完成任務的最合理原因是run()的正常結束。 任務流結束,然后任務由各自的所有者線程釋放。 一旦線程釋放任務,只要有任務,它就准備好執行另一個任務。

檢查這種情況的一種直接方法是在run()方法的末尾記錄一些內容:

class MyRunnable implements Runnable {

    public void run() {
        while(/*some condition*/) {
           // do my things
        }
        log.info("My Runnable has finished for now!");
    }
}

如果記錄一條消息還不夠,您可以調用另一個對象的方法。

木頭下的例外

另一個(最)可能的原因是任務執行期間拋出的未捕獲異常。 在線程池中,像這樣的未經檢查的異常將突然停止方法執行並(出人意料地)被吞入java.util.concurrent.FutureTask對象中。 為了避免這樣的事情,我使用以下習語:

class MyRunnable implements Runnable {
    public void run() {
        while(/*some condition*/) {
            try {
                // do my things
            } catch (Throwable throwable) {
                handle(throwable);
            }
        }
        log.info("My Runnable has finished for now!");
    }

    private void handle(Throwable throwable) {
        // ...
    }
}

或者根據我也使用的邏輯/性能要求:

    public void run() {
        try {
            while(/*some condition*/) {
                // do my things
            }
        } catch (Throwable throwable) {
            handle(throwable);
        }
        System.out.println("My Runnable has finished for now!");
    }

下面的代碼舉例說明了這里評論的問題:

package mypocs;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ExceptionSwallowingInThreadPoolsPoC {

  public static void main(String[] args) {

    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

    final Object LOCK = new Object();

    threadPoolExecutor.submit(() -> {
      while (true) {
        synchronized (LOCK) {
          System.out.println("Thread 'A' never ends");
        }
        Thread.sleep(1000L);
      }
    });

    threadPoolExecutor.submit(() -> {
      int lifespan = 3;
      while (lifespan > 0) {
    synchronized (LOCK) {
              System.out.println("Thread 'B' is living for " + lifespan + " seconds");
        }
        lifespan--;
        try {
          Thread.sleep(1000L);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println("Thread 'B' finished");
    });

    threadPoolExecutor.submit(() -> {
      int lifespan = 3;
      while (lifespan > 0) {
        synchronized (LOCK) {
          System.out.println("Thread 'C' is living for " + lifespan + " seconds");
        }
        lifespan--;

        if (lifespan < 1) {
          throw new RuntimeException("lifespan reached zero");
        }

        try {
          Thread.sleep(1000L);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println("Thread 'C' finished");
    });

    while (true) {
      try {
        Thread.sleep(1000L);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      synchronized (LOCK) {
        System.out.println("==== begin");
        System.out.println("getActiveCount: " + threadPoolExecutor.getActiveCount());
        System.out.println("getCompletedTaskCount: " + threadPoolExecutor.getCompletedTaskCount());
        System.out.println("getPoolSize: " + threadPoolExecutor.getPoolSize());
        System.out.println("==== end");
      }
    }

  }

}

代碼應該輸出如下內容:

Thread 'A' never ends
Thread 'B' is living for 3 seconds
Thread 'C' is living for 3 seconds
Thread 'C' is living for 2 seconds
==== begin
getActiveCount: 3
getCompletedTaskCount: 0
getPoolSize: 3
==== end
Thread 'B' is living for 2 seconds
Thread 'A' never ends
==== begin
getActiveCount: 3
getCompletedTaskCount: 0
getPoolSize: 3
==== end
Thread 'C' is living for 1 seconds
Thread 'B' is living for 1 seconds
Thread 'A' never ends
Thread 'B' finished
==== begin
getActiveCount: 1
getCompletedTaskCount: 2
getPoolSize: 3
==== end
Thread 'A' never ends
Thread 'A' never ends
...

暫無
暫無

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

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