简体   繁体   中英

Java - Thread stuck in “Park” status

I'm having trouble getting over 100 threads to run simultaneously. When I do a thread dump, I noticed that many of them are in parked status , ie

parking to wait for <0x00000000827e1760> (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject).

The program runs fine with about 25 threads or less. Is there a way ti identify what's causing the concurrent lock, and/or prevent it? This was running in a fixed pool size of 200 using the Executor service.

Apologies for the lack of code - it's proprietary and there's a lot to be changed to obfuscated it.

The class (ConditionObject) you are referring to is used to lock objects from being accessed concurrently by multiple threads. The Javadoc doesn't describe the thread state you mention, but here is my guess:

Your locked object is being blocked by one thread so long, that the other threads start to pile up on the lock. Once the thread holding the lock releases it, the next thread continues the aquire the lock. Until that new thread has done his work, new threads pile up behing the lock.

If my guess is right, then could:

  • reduce the time that each thread spends in the lock, or
  • distribute the threads on different locked things (if your problem permits that), or
  • you use an implementation that doesn't require locking.

Without knowing your problem domain, I hope that the information above is enough to point you into some direction that might be of help for you.

Are you using some sort of ThreadPoolExecutor such as the ones provided by java.util.concurrent.Executors class? Perhaps you are facing a case of tasks being finished by silently uncaught exceptions. The dump fragment looks like an inactive pooled thread and one reason to get an inactive thread (which should be active) is an exception throwed up but surrounded by the default thread pool implementation.

LockSupport.park()

In thread pools, THREADS waiting for a TASK are locked out by LockSupport.park(); . See java.util.concurrent.locks.AbstractQueuedSynchronizer source from openjdk :

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

It means that the TASK which the THREAD were executing finished (abruptaly or not) and now the thread is waiting for another task to execute (see java.util.concurrent.ThreadPoolExecutor openjdk source ):

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

As one can see, the thread is locked out in the call workQueue.take(); .

Thus, shortly, threads in "parked status" are just waiting for new tasks after the previous ones have finished.

Why does my task is no longer running?

The most reasonable cause of a finished task is the regular end of the run() . The task flow finishes and then the task is released by the respective owner thread. Once the thread releases the task, it is ready to execute another task as long there is one.

A straightforward way to check this scenario is by logging something in the end of the run() method:

class MyRunnable implements Runnable {

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

If log a message is not enough you can call a method of another object instead.

Exceptions under the wood

Another (most) probable cause is an uncaught exception thrown during the task execution. Within a thread pool, an unchecked exception like this will abruptaly stop the method execution and (surprisely) be swallowed into a java.util.concurrent.FutureTask object. In order to avoid things like this, I use the following idiom:

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) {
        // ...
    }
}

or depending on the logic/performance requirements I also use:

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

The code below exemplify the issues commented here in action:

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");
      }
    }

  }

}

The code should output something like:

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
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM