Android 线程池的封装

文章详细介绍了如何在Java中创建和初始化一个全局线程池,包括核心线程数、最大线程数、存活时间等参数设置,并讨论了不同类型的线程池(如FixedThreadPool、CachedThreadPool等)的适用场景。线程池的使用可以有效管理线程,避免资源浪费和线程安全问题,同时提供了处理线程拒绝的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * 全局使用的线程池
 */
public class GlobalThreadPools {

    private static String TAG = GlobalThreadPools.class.getSimpleName();

    private static ExecutorService THREAD_POOL_EXECUTOR;//线程池

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU数量
    private static final int CORE_POOL_SIZE = CPU_COUNT;//核心线程数
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2;//最大线程数
    private static final int KEEP_ALIVE_SECONDS = 60;//线程闲置后的存活时间
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(CPU_COUNT);//任务队列
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {//线程工厂
        private final AtomicInteger mCount = new AtomicInteger(1);
        public Thread newThread(Runnable r) {
            return new Thread(r, "MangoTask #" + mCount.getAndIncrement());
        }
    };

    //初始化线程池
    private void initThreadPool() {
        THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory, new RejectedHandler()){
            @Override
            public void execute(Runnable command) {
                super.execute(command);
                Log.e(TAG,"ActiveCount="+getActiveCount());
                Log.e(TAG,"PoolSize="+getPoolSize());
                Log.e(TAG,"Queue="+getQueue().size());
            }
        };
    }

    private static class RejectedHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //可在这里做一些提示用户的操作
//            Log.v("+++","is over the max task...");
        }
    }

    private static GlobalThreadPools instance;
    private GlobalThreadPools(){
        initThreadPool();
    }
    public static GlobalThreadPools getInstance(){
        if (instance == null) {
            instance = new GlobalThreadPools();
        }
        return instance;
    }

    public void execute(Runnable command){
        THREAD_POOL_EXECUTOR.execute(command);
    }
/**
     * @param command 外部创建的Runnable
     * (一堆人去一家很大的咖啡馆喝咖啡)
     * 非固定大小线程池
     * 只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程
     *                (60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务,任何任务都会被立即执行。
     *                因为有超时机制,空闲的线程最终都会被回收,几乎不占用什么资源
     */
    private void CachedThreadPool(Runnable command){
        if(cachedThreadPool == null){
            cachedThreadPool = Executors.newCachedThreadPool();
        }
        cachedThreadPool.execute(command);
    }

    /**
     * @param command 外部创建的Runnable
     *(一堆人排队上公厕)
     * 可复用固定大小线程池
     * 只有核心线程,并且数量是固定的,也不会被回收销毁,能更快地响应外界请求。所有线程都活动时,因为队列没有限制大小,新任务会等待执行。
     *                所有任务都在核心线程中执行,核心线程一旦创建,即使任务执行完毕,也一直处于闲置状态,不会被销毁,线程池会一直对这个线程进行维护,
     *                直到线程关闭。如果所有的核心线程都处于活动状态,新加入的任务会被放到linkedblockingqueue这个队列中,队列没有大小限制
     *                因为核心线程创建后不会被销毁,当新的任务加入时,不需要重新创建线程来执行新任务,所以响应速度非常快。
     *                因为核心线程不会销毁,长时间处于闲置状态,会消耗资源,一般用于不会有太多任务的场景
     *  参数:nThreads(最大核心线程数量)
     */
    private void FixThreadPool(Runnable command){
        if(fixThreadPool == null){
            fixThreadPool = Executors.newFixedThreadPool(3);
        }
        fixThreadPool.execute(command);
    }

    /**
     * @param command 外部创建的Runnable
     *(4个里面唯一一个有延迟执行和周期重复执行的线程池)
     * 核心线程数固定,非核心线程数没有限制,主要用于执行定时任务以及有固定周期的重复任务。
     *
     *                两个执行方法调用:
     *                  任务时间小于周期时间,则按照给定的周期时间进行,两个方法表现一致。
     *                  任务时间大于周期时间,这两个方法执行方式则不同。
     *
     *                .scheduleAtFixedRate()    // 执行完上一个任务,用时超过了周期时间,会立即执行下一个任务。
     *                .scheduleWithFixedDelay() // 执行完上一个任务,用时超过了周期时间,还会等待周期时间,再去执行下一个任务
     *
     */
    private void ScheduledThreadPool(Runnable command){
        if (scheduledThreadPool == null) {
            scheduledThreadPool = Executors.newScheduledThreadPool(3);// 核心线程数
        }
//        scheduledThreadPool.execute(command);
        // 使用,延迟1秒执行,每隔2秒执行一次Runnable r
        scheduledThreadPool.scheduleAtFixedRate(command,1000,2000,TimeUnit.MILLISECONDS);
//        scheduledThreadPool.scheduleWithFixedDelay(command,1000,2000,TimeUnit.MILLISECONDS);
    }

    /**
     * @param command 外部创建的Runnable
     * (公厕里只有一个坑位)
     *  只有一个核心线程,其他任务放在阻塞队列中,这个队列没有大小限制,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。
     */
    private void SingleThreadPool(Runnable command){
        if(singleThreadPool == null){
            singleThreadPool = Executors.newSingleThreadExecutor();
        }
        singleThreadPool.execute(command);
    }

//    /**
//     * 通过interrupt方法尝试停止正在执行的任务,但是不保证真的终止正在执行的任务
//     * 停止队列中处于等待的任务的执行
//     * 不再接收新的任务
//     * @return 等待执行的任务列表
//     */
//    public List<Runnable> shutdownNow(){
//        return THREAD_POOL_EXECUTOR.shutdownNow();
//    }
//
//    /**
//     * 停止队列中处于等待的任务
//     * 不再接收新的任务
//     * 已经执行的任务会继续执行
//     * 如果任务已经执行完了没有必要再调用这个方法
//     */
//    public void shutDown(){
//        THREAD_POOL_EXECUTOR.shutdown();
//        sPoolWorkQueue.clear();
//    }

}

调用:

//线程池开启任务
        GlobalThreadPools.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                  .......
            }
        });

线程池
线程池概念来源于Java中的Executor,它是一个接口,真正的实现为ThreadPoolExecutor。ThreadPoolExecutor提供了一系列参数来配置线程池。
优点
1:重用线程池中的线程,线程在执行完任务后不会立刻销毁,而会等待另外的任务,这样就不会频繁地创建、销毁线程和调用GC。。
2:有效控制线程池的最大并发数,避免大量线程抢占资源出现的问题。
3:对多个线程进行统一地管理,可提供定时执行及指定间隔循环执行的功能。
ThreadPoolExecutor 有多个重载方法,但最终都调用了这个构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数:
corePoolSize:线程池中核心线程的数量;为了内存优化,在线程池维护了几个重要的线程,不达到一定条件不开辟其余线程
maximumPoolSize :线程池中最大线程数量:这个数量是包括核心线程的,当线程池中的正在执行的线程池达到了这个数字,再提交线程如果你不做特殊处理将会抛出异常
keepAliveTime:非核心线程的超时时长;当线程池中的非核心线程闲置时间超过这个值代表的时间后,将会被回收;同时如果调用ThreadPoolExecutor.allowCoreThreadTimeOut(true),那么核心线程也会符合这个设置
unit:keepAliveTime值的单位,可以是时分秒等
workQueue:存放待执行的线程;你通过execute方法提交线程,但是这些线程还没达到执行条件,那么就会保存在这个队列里
threadFactory:创建线程池的工厂;在这个工厂里,我们可以指定线程的一些信息
handler:线程提交拒绝策略;通常是线程池中的正在执行的线程数量已经达到了最大线程数或线程池关闭,如果不传,默认是抛出一个RejectedExecutionException,所以最好传下
推荐使用 Executors 的工厂方法来创建线程池,通过直接或间接的配置 ThreadPoolExecutor 的参数来构建线程池,常用的线程池有如下 4 种,newFixedThreadPool ,newCachedThreadPool,
newScheduledThreadPool 和 newSingleThreadExecutor。

FixedThreadPool 只有核心线程,并且数量是固定的,也不会被回收,能更快地响应外界请求。
SingleThreadPool 只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。
CachedThreadPool 只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程处理任务,任何任务都会被立即执行。
ScheduledThreadPool 核心线程数固定,非核心线程数没有限制,主要用于执行定时任务以及有固定周期的重复任务。

ThreadPoolExecutor 执行任务时大致遵循如下流程:
1.如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。
2.如果线程池中的线程数已经达到核心线程数,且任务队列workQueue未满,则将新线程放入workQueue中等待执行。
3.如果线程池中的线程数已经达到核心线程数但未超过线程池规定最大值,且workQueue已满,则开启一个非核心线程来执行任务。
4.如果线程池中的线程数已经超过线程池规定最大值,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。
线程池大小:(N为CPU数量)
如果是CPU密集型应用,则线程池大小设置为N+1
如果是IO密集型应用,则线程池大小设置为2N+1
I/O密集型
指的是系统的CPU效能相对硬盘/内存的效能要好,大部分的状况是 CPU 在等 I/O (硬盘/内存) 的读/写, CPU Loading 不高。
CPU密集型
指的是系统的 硬盘/内存 效能 相对 CPU 的效能 要好,大部分的状况是 CPU Loading 100%,CPU 要读/写 I/O (硬盘/内存),I/O在很短的时间就可以完成,而 CPU 还有许多运算要处理,CPU Loading 很高。
获取CPU数量的方法为:
Runtime.getRuntime().availableProcessors();

摘要自:https://www.jianshu.com/p/2cf20857b6ab/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值