Skip to content

源码:labs/java/runtime-concurrency/thread-pool-basics/src/AndroidThreadPoolPresets.java

  • 原始路径:labs/java/runtime-concurrency/thread-pool-basics/src/AndroidThreadPoolPresets.java
  • 类型:java
java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Android 侧常见 ThreadPoolExecutor 预设与 Executors 工厂方法对比。
 *
 * 运行:
 * cd labs/java/runtime-concurrency/thread-pool-basics
 * javac -d out src/AndroidThreadPoolPresets.java
 * java -cp out AndroidThreadPoolPresets
 */
public class AndroidThreadPoolPresets {

    public static void main(String[] args) throws Exception {
        System.out.println("====== 1. Executors 工厂方法底层参数对比 ======");
        printExecutorsFactoryInternals();

        System.out.println("\n====== 2. newFixedThreadPool:线程不涨、任务进无界队列 ======");
        demoFixedPoolQueueGrowth();

        System.out.println("\n====== 3. newCachedThreadPool:不排队、优先扩线程 ======");
        demoCachedPoolThreadGrowth();

        System.out.println("\n====== 4. Android 推荐:图片压缩池(AbortPolicy,满则拒绝)======");
        demoPreset("图片压缩池", createImageCompressPool(), 25, 100);

        System.out.println("\n====== 4b. 备选:图片压缩池 + CallerRunsPolicy(仅后台提交路径可用)======");
        demoCallerRunsImageCompressPoolNote();

        System.out.println("\n====== 5. Android 推荐:IO 池(阻塞型任务,队列略大)======");
        demoPreset("IO 池", createIoPool(), 5, 50);

        System.out.println("\n====== 6. Android 推荐:串行日志池(单线程顺序写)======");
        demoPreset("串行日志池", createSerialLogPool(), 4, 30);

        System.out.println("\n====== 7. Android 推荐:实时预览池(DiscardOldest,新任务优先)======");
        demoPreset("实时预览池", createPreviewPool(), 6, 40);
    }

    // -------------------------------------------------------------------------
    // Executors 工厂方法:只看参数就能理解风险
    // -------------------------------------------------------------------------

    static void printExecutorsFactoryInternals() throws InterruptedException {
        ThreadPoolExecutor fixed = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
        ThreadPoolExecutor cached = (ThreadPoolExecutor) Executors.newCachedThreadPool();

        System.out.println("[newFixedThreadPool(2)]");
        printPoolSpec(fixed, "固定 n 线程 + 默认 LinkedBlockingQueue(容量约 21 亿)→ 高峰任务堆队列,易 OOM");

        System.out.println("[newCachedThreadPool()]");
        printPoolSpec(cached, "core=0, max=Integer.MAX_VALUE + SynchronousQueue → 高峰疯狂建线程,易 OOM");

        shutdownQuietly(fixed);
        shutdownQuietly(cached);
    }

    static void demoFixedPoolQueueGrowth() throws InterruptedException {
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
        submitSlowTasks(pool, 5, "fixed-task");
        System.out.println("提交 5 个慢任务后 | 活跃线程: " + pool.getActiveCount()
                + " | 队列堆积: " + pool.getQueue().size()
                + " (线程数仍为 2,多出来的全在队列里等)");
        shutdownQuietly(pool);
    }

    static void demoCachedPoolThreadGrowth() throws InterruptedException {
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        submitSlowTasks(pool, 5, "cached-task");
        System.out.println("提交 5 个慢任务后 | 池大小: " + pool.getPoolSize()
                + " | 活跃线程: " + pool.getActiveCount()
                + " | 队列堆积: " + pool.getQueue().size()
                + " (SynchronousQueue 不排队,倾向于直接扩线程)");
        shutdownQuietly(pool);
    }

    // -------------------------------------------------------------------------
    // Android 工程里更常见的显式 ThreadPoolExecutor 预设
    // -------------------------------------------------------------------------

    /**
     * 图片压缩 / 解码(默认推荐):CPU + 少量 IO,保守线程数 + 小队列。
     * 池满时 {@link ThreadPoolExecutor.AbortPolicy} 抛
     * {@link RejectedExecutionException},
     * 上层应 try-catch 后提示用户、排队重试或降级——避免在主线程同步执行重活。
     */
    static ThreadPoolExecutor createImageCompressPool() {
        return newImageCompressPool(new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * 图片压缩池的 CallerRuns 备选方案。
     * <p>
     * <b>仅当能保证所有 {@code execute/submit} 都来自后台线程时</b>才可使用:
     * 池满时由提交线程同步执行任务,形成自然背压。
     * 若从主线程提交,触发后会在 UI 线程跑压缩逻辑,极易 ANR。
     */
    static ThreadPoolExecutor createImageCompressPoolWithCallerRuns() {
        return newImageCompressPool(new ThreadPoolExecutor.CallerRunsPolicy());
    }

    static ThreadPoolExecutor newImageCompressPool(RejectedExecutionHandler handler) {
        int cores = Runtime.getRuntime().availableProcessors();
        int core = Math.max(2, cores / 2);
        int max = Math.max(core, cores);
        return new ThreadPoolExecutor(
                core,
                max,
                30L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(8),
                namedFactory("img-compress"),
                handler);
    }

    /**
     * 本地 DB / 文件 / 网络回调后的磁盘写入:线程可略多,队列中等,失败要可见。
     */
    static ThreadPoolExecutor createIoPool() {
        int cores = Runtime.getRuntime().availableProcessors();
        int core = Math.max(4, cores * 2);
        int max = core + 1;
        return new ThreadPoolExecutor(
                core,
                max,
                60L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(64),
                namedFactory("io-worker"),
                new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * 异步日志 / 埋点落盘:单线程保证顺序,队列满则丢低价值日志。
     */
    static ThreadPoolExecutor createSerialLogPool() {
        return new ThreadPoolExecutor(
                1,
                1,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(200),
                namedFactory("log-writer"),
                new ThreadPoolExecutor.DiscardPolicy());
    }

    /**
     * 缩略图 / 列表项预览:旧帧可丢,新提交更重要。
     */
    static ThreadPoolExecutor createPreviewPool() {
        return new ThreadPoolExecutor(
                2,
                4,
                10L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4),
                namedFactory("preview"),
                new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    // -------------------------------------------------------------------------
    // 演示辅助
    // -------------------------------------------------------------------------

    static void demoCallerRunsImageCompressPoolNote() throws InterruptedException {
        ThreadPoolExecutor pool = createImageCompressPoolWithCallerRuns();
        System.out.println("[图片压缩池-CallerRuns 备选] core=" + pool.getCorePoolSize()
                + ", max=" + pool.getMaximumPoolSize()
                + ", policy=CallerRunsPolicy");
        System.out.println("  → 仅后台提交路径可用;池满时由提交线程同步执行,主线程提交有 ANR 风险。");
        System.out.println("  → 默认推荐 createImageCompressPool()(AbortPolicy),失败由业务层重试/降级。");
        shutdownQuietly(pool);
    }

    static void demoPreset(String label, ThreadPoolExecutor pool, int taskCount, long workMs)
            throws InterruptedException {
        System.out.println("[" + label + "] core=" + pool.getCorePoolSize()
                + ", max=" + pool.getMaximumPoolSize()
                + ", queue=" + pool.getQueue().getClass().getSimpleName()
                + ", policy=" + pool.getRejectedExecutionHandler().getClass().getSimpleName());

        AtomicInteger rejected = new AtomicInteger();
        for (int i = 1; i <= taskCount; i++) {
            final int taskId = i;
            try {
                pool.execute(() -> {
                    try {
                        Thread.sleep(workMs);
                        System.out.println("  " + label + " | 任务 " + taskId
                                + " 完成于 [" + Thread.currentThread().getName() + "]");
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            } catch (RejectedExecutionException e) {
                rejected.incrementAndGet();
                System.out.println("  " + label + " | 任务 " + taskId + " 被拒绝 ("
                        + pool.getRejectedExecutionHandler().getClass().getSimpleName() + ")");
            }
        }

        Thread.sleep(150);
        System.out.println("  提交后快照 | 活跃: " + pool.getActiveCount()
                + " | 队列: " + pool.getQueue().size()
                + " | 池大小: " + pool.getPoolSize()
                + " | 拒绝次数: " + rejected.get());

        shutdownQuietly(pool);
    }

    static void submitSlowTasks(ThreadPoolExecutor pool, int count, String prefix)
            throws InterruptedException {
        for (int i = 1; i <= count; i++) {
            final int taskId = i;
            pool.execute(() -> {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("  " + prefix + "-" + taskId + " 完成于 ["
                        + Thread.currentThread().getName() + "]");
            });
        }
        Thread.sleep(50);
    }

    static ThreadFactory namedFactory(String prefix) {
        AtomicInteger seq = new AtomicInteger(1);
        return r -> {
            Thread t = new Thread(r, prefix + "-" + seq.getAndIncrement());
            t.setDaemon(true);
            return t;
        };
    }

    static void printPoolSpec(ThreadPoolExecutor pool, String risk) {
        String queueType = pool.getQueue().getClass().getSimpleName();
        String queueDetail = queueType;
        if (pool.getQueue() instanceof LinkedBlockingQueue) {
            queueDetail += "(默认近无界)";
        } else if (pool.getQueue() instanceof SynchronousQueue) {
            queueDetail += "(零容量,不排队)";
        } else if (pool.getQueue() instanceof ArrayBlockingQueue) {
            queueDetail += "(有界)";
        }
        System.out.println("  core=" + pool.getCorePoolSize()
                + ", max=" + pool.getMaximumPoolSize()
                + ", queue=" + queueDetail
                + ", keepAlive=" + pool.getKeepAliveTime(TimeUnit.SECONDS) + "s");
        System.out.println("  → " + risk);
    }

    static void shutdownQuietly(ExecutorService pool) throws InterruptedException {
        pool.shutdown();
        pool.awaitTermination(3, TimeUnit.SECONDS);
    }
}