Subscribed unsubscribe Subscribe Subscribe

Islands in the byte stream

Technical notes by a software engineer

AndroidのThreadPoolExecutorに関する覚書

ThreadPoolExecutor を調べていて、 Thread インスタンスの最大数はどんなものだろうと思ったので確認したところ、端末によって異なりますが1000個〜10000個程度でした。これを超えるとOutOfMemoryErrorが発生したり、最悪の場合いきなりクラッシュしたりします。

Executors.newCachedThreadPool()ThreadPoolExecutor を生成するファクトリメソッドですが、スレッド生成の最大数が Integer.MAX_VALUE (つまり無制限)となっています。このexecutorによってスレッドを数百以上生成することはまずないとは思いますが、上限が定められていない以上うっかり端末の限界値を超えないとも限りません。AndroidExecutors.newCachedThreadPool() を使うのはやめたほうがよさそうです。

ところで AsyncTaskのソースコードをみると、executorは以下のようになっています。

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

CPU_COUNT + 1個のスレッドが常時存在し、それで処理しきれないほどのタスクは最大CPU_COUNT * 2 + 1 個まで増えて、その増えたスレッドは1秒でシャットダウンされる(つまりほぼ再利用はされない)、と読めます。Android端末はコア数はそんなに多くないですし、こんなものかなという気はします。

Thread生成限界の確認コード:

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    final int ii = i;
                    final long t0 = System.currentTimeMillis();
                    new Thread() {
                        @Override
                        public void run() {
                            Log.d("XXX",
                                    "i=" + ii + ", creation time: " + (System.currentTimeMillis()
                                            - t0)
                                            + "ms");
                            try {
                                Thread.sleep(10000000000L);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }.start();
                }

            }
        }, 1000);