Android How to Adjust Thread Call Stack Size

  • 2021-12-09 10:03:05
  • OfStack

In the conventional Android development process, as the business logic becomes more and more complex, the call stack may become deeper and deeper, and it is inevitable that the call stack will cross the boundary. In this case, it is necessary to adjust the size of the thread stack.

Of course, it is mainly to increase the thread stack size, especially in the case of jni calls, and the stack overhead of C + + layer is sometimes very horrible, such as recursive calls.

This needs to be divided into three situations: main thread, custom thread pool and AsyncTask.

There is no way to modify the thread stack of the main thread, so there is no way to deal with it.

In the case of thread pool, you need to call the constructor when you create a thread


public Thread(@RecentlyNullable ThreadGroup group, @RecentlyNullable Runnable target, @RecentlyNonNull String name, long stackSize)

By setting stackSize Parameter to solve the problem.

The reference code is as follows:


import android.support.annotation.NonNull;
import android.util.Log;

import java.util.concurrent.ThreadFactory;

/**
 * A ThreadFactory implementation which create new threads for the thread pool.
 */
public class SimpleThreadFactory implements ThreadFactory {
  private static final String TAG = "SimpleThreadFactory";
  private final static ThreadGroup group = new ThreadGroup("SimpleThreadFactoryGroup");
  //  The worker thread stack size is adjusted to 2MB
  private final static int workerStackSize = 2 * 1024 * 1024;

  @Override
  public Thread newThread(@NonNull final Runnable runnable) {
    final Thread thread = new Thread(group, runnable, "PoolWorkerThread", workerStackSize);
    // A exception handler is created to log the exception from threads
    thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      @Override
      public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
        Log.e(TAG, thread.getName() + " encountered an error: " + ex.getMessage());
      }
    });

    return thread;
  }
}

import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * A Singleton thread pool
 */
public class ThreadPool {
  private static final String TAG = "ThreadPool";
  private static final int KEEP_ALIVE_TIME = 1;
  private static volatile ThreadPool sInstance = null;
  private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
  private final ExecutorService mExecutor;
  private final BlockingQueue<Runnable> mTaskQueue;

  // Made constructor private to avoid the class being initiated from outside
  private ThreadPool() {
    // initialize a queue for the thread pool. New tasks will be added to this queue
    mTaskQueue = new LinkedBlockingQueue<>();

    Log.d(TAG, "Available cores: " + NUMBER_OF_CORES);

    mExecutor = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, TimeUnit.SECONDS, mTaskQueue, new SimpleThreadFactory());
  }

  @NonNull
  @AnyThread
  public static ThreadPool getInstance() {
    if (null == sInstance) {
      synchronized (ThreadPool.class) {
        if (null == sInstance) {
          sInstance = new ThreadPool();
        }
      }
    }
    return sInstance;
  }

  private boolean isThreadPoolAlive() {
    return (null != mExecutor) && !mExecutor.isTerminated() && !mExecutor.isShutdown();
  }

  @Nullable
  @AnyThread
  public <T> Future<T> submitCallable(@NonNull final Callable<T> c) {
    synchronized (this) {
      if (isThreadPoolAlive()) {
        return mExecutor.submit(c);
      }
    }
    return null;
  }

  @Nullable
  @AnyThread
  public Future<?> submitRunnable(@NonNull final Runnable r) {
    synchronized (this) {
      if (isThreadPoolAlive()) {
        return mExecutor.submit(r);
      }
    }
    return null;
  }

  /* Remove all tasks in the queue and stop all running threads
   */
  @AnyThread
  public void shutdownNow() {
    synchronized (this) {
      mTaskQueue.clear();
      if ((!mExecutor.isShutdown()) && (!mExecutor.isTerminated())) {
        mExecutor.shutdownNow();
      }
    }
  }
}

In the case of AsyncTask, 1 is generally done by calling


public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)

Specify a thread pool to run, and resize the thread stack in a specific thread pool.

The reference code is as follows:


import android.os.AsyncTask;
import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
import android.util.Log;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public abstract class AsyncTaskEx<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

  private static final String TAG = "AsyncTaskEx";
  private static final int KEEP_ALIVE_TIME = 1;
  private static volatile ThreadPool sInstance = null;
  private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
  private final ExecutorService mExecutor;
  private final BlockingQueue<Runnable> mTaskQueue;

  public AsyncTaskEx() {
    // initialize a queue for the thread pool. New tasks will be added to this queue
    mTaskQueue = new LinkedBlockingQueue<>();

    Log.d(TAG, "Available cores: " + NUMBER_OF_CORES);

    mExecutor = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES * 2, KEEP_ALIVE_TIME, TimeUnit.SECONDS, mTaskQueue, new SimpleThreadFactory());
  }

  public AsyncTask<Params, Progress, Result> executeAsync(@NonNull final Params... params) {
    return super.executeOnExecutor(mExecutor, params);
  }

  /* Remove all tasks in the queue and stop all running threads
   */
  @AnyThread
  public void shutdownNow() {
    synchronized (this) {
      mTaskQueue.clear();
      if ((!mExecutor.isShutdown()) && (!mExecutor.isTerminated())) {
        mExecutor.shutdownNow();
      }
    }
  }
}

Reference link

Increase AsyncTask stack size? StackOverFlowError: Stack size 1036KB in AsyncTask Android: Increasing call stack size AsyncTask and Thread Pool

The above is Android how to adjust the size of the thread call stack details, more about Android adjust the size of the call stack information please pay attention to other related articles on this site!


Related articles: