Android 线程池的使用与说明

线程池是多线程的一种处理方法。线程的创建和销毁需要消耗系统资源,使用线程池可以避免频繁的创建和销毁线程,具有以下优点:降低系统资源的消耗;提高响应速度;合理地管理线程,提高系统的稳定性。

Android采用的是Java的线程池。Java线程池的核心类是ThreadPoolExecutor,使用ThreadPoolExecutor创建线程池。下面对其进行详细说明。

corePoolSize & maximumPoolSize,核心线程数和最大线程数
线程池根据这两个参数自动调整线程的数量,当提交一个新的任务时,调整策略如下:
1、currentPoolSize < corePoolSize,创建一个新的线程。
2、corePoolSize <= currentPoolSize < maximumPoolSize,如果任务队列未满,加入任务队列等待;如果任务队列已满,创建一个新的线程。

创建线程
使用ThreadFactory创建线程,默认使用Executors.defaultThreadFactory(),创建的线程具有相同的优先级和non-daemon status,在同一个线程组。如果需要设定优先级,group,daemon status,请使用其他的ThreadFactory,线程需要处理RuntimePermission modifyThread,否则服务会被降级:设置的参数不能及时生效,线程池被关闭后可能停留在结束状态但是任务未完成。

On-demand 创建
默认情况下,只有任务到达时才会创建并启动线程。但通过方法prestartCoreThread()或者prestartAllCoreThreads()可以提前启动线程。

Keep-alive times
设置线程的闲置时长,当线程的闲置时长超过这个参数值并且currentPoolSize > corePoolSize,idle线程会被回收。
设置allowCoreThreadTimeOut(true),currentPoolSize <= corePoolSize,idle线程会被回收。

Queuing 等待队列
1、currentPoolSize < corePoolSize,创建一个新的线程执行任务,不会将任务加入等待队列。
2、currentPoolSize >= corePoolSize,将任务加入等待队列。
3、等待队列已满,currentPoolSize < maximumPoolSize,创建一个新的线程执行任务。
有三种等待策略:
1、SynchronousQueue,不保存提交的任务,直接新建线程来执行新任务,不需要进行等待,不能设置有边界的maximumPoolSize,否则新的任务可能被拒绝。适用于任务之间有相互的依赖。
2、LinkedBlockingQueue,基于链表的阻塞队列。如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE, 此时maximumPoolSize不起作用,最多有corePoolSize个线程在工作,适用于任务之间无依赖。
3、ArrayBlockingQueue,基于数组的有界阻塞队列, 线程数量不会超过maximumPoolSize。

拒绝策略
线程池已经关闭或者线程数量已满的情况下,提交新的任务会被拒绝。execute方法将调用RejectedExecutionHandler的rejectedExecution方法处理任务。有四种策略:
1、ThreadPoolExecutor.AbortPolicy,默认策略,丢弃任务并抛出异常RejectedExecutionException
2、ThreadPoolExecutor.CallerRunsPolicy,让调用者自己执行任务。
3、ThreadPoolExecutor.DiscardPolicy,直接丢弃。
4、ThreadPoolExecutor.DiscardOldestPolicy,丢弃队列头部的任务,再尝试,可能会重复丢弃-尝试。

线程池回收
当线程池内没有线程,并且不再被引用时,会自动回收。

线程池的关闭
ThreadPoolExecutor提供了两个方法,用于线程池的关闭。
shutdown():不会立即终止线程池,而是要等所有缓存队列中的任务都执行完后才终止,但不会接受新的任务。
shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

线程池的使用实例
在本例中,使用了ArrayBlockingQueue,拒绝策略使用ThreadPoolExecutor.DiscardPolicy。核心线程数是3,最大线程数是5,等待列大小为5。首先创建3个核心线程执行任务,接下来的任务放入等待队列,当等待队列满的时候,继续创建新的线程执行任务,线程数达到最大值5并且缓存队列已满的情况下,丢弃新到达的任务。

private void threadPool() {
    int corePoolSize = 3;
    int maximumPoolSize = 5;
    int keepLiveTime = 50;
    BlockingQueue blockingQueue = new ArrayBlockingQueue<Runnable>(5);

    ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull Runnable r) {
            ThreadGroup threadGroup = new ThreadGroup("worker");
            Thread thread = new Thread(threadGroup, r);
            thread.setDaemon(false);
            thread.setPriority(Thread.NORM_PRIORITY);
            return thread;
        }
    };

    RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

    mThreadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
            keepLiveTime, TimeUnit.SECONDS, blockingQueue, threadFactory, handler);

    for (int i = 0; i < 12; i++) {
        Runnable runnable = new Task(i);
        mThreadPoolExecutor.execute(runnable);
        Log.e(TAG, "current thread count:" + mThreadPoolExecutor.getPoolSize()
                + " number:" + i);
    }
}

private class Task implements Runnable {
    int taskId;

    Task(int id) {
        taskId = id;
    }

    @Override
    public void run() {
        try {
            Log.d(TAG, "begin run in task:" + taskId);
            Thread.sleep(10000);
        }catch (Exception e) {

        }
    }
}

日志如下:
在这里插入图片描述
线程池的创建方法
Java中,不建议直接使用ThreadPoolExecutor创建线程池,提供了以下工厂方法创建线程池:
Executors.newSingleThreadExecutor() 线程池中线程的数量为1, 等待队列的大小为Integer.MAX_VALUE。
Executors.newCachedThreadPool() 线程的数量为Integer.MAX_VALUE,不保存任务。
Executors.newFixedThreadPool(int) 线程池中线程的数量为固定值,等待队列的大小为Integer.MAX_VALUE。
Executors.newScheduledThreadPool(int) 定时线程池。
四个方法的实现如下:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
            60L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());
}

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
}

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类。可用于实现定时工作。实例如下:

private void scheduledThreadPool() {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Log.e(TAG, format.format(new Date()));

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e(TAG, "begin:" + format.format(new Date()));
            try {
                Thread.sleep(10000);
            } catch (Exception e) {

            }
            Log.e(TAG, "end  :" + format.format(new Date()));
        }
    };
    
    //重复执行,初始延迟5秒执行,每一次任务执行时间指定为16秒(period参数)。
    //如果period小于任务执行时间,则上一次任务结束后执行下一次任务。
    //如果period大于任务执行时间,任务结束后等待period-任务执行时间,再执行下一次任务。
    executorService.scheduleAtFixedRate(runnable, 5, 16, TimeUnit.SECONDS);

    //只执行一次,延迟5秒执行
    executorService.schedule(runnable, 5, TimeUnit.SECONDS);

    //重复执行,初始延迟5秒执行,上一次任务执行结束后再等待16秒执行下一次任务。
    //也就是任务之间的间隔是16秒。
    executorService.scheduleWithFixedDelay(runnable, 5, 16, TimeUnit.SECONDS);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值