Android线程池

来源:互联网 发布:sql查询总金额 编辑:程序博客网 时间:2024/05/29 09:13

new Thread().start()的弊病

因为不能在UI中执行耗时操作,所以需要用到子线程,在平常使用的过程中,常常会使用下面这种方式执行。

        new Thread(new Runnable() {            @Override            public void run() {                // do something            }        }).start();

使用这种方式执行子线程存在如下三个弊病:

资源浪费

在操作系统中,大量的线程存在,会影响性能,因为CPU在线程之间的切换也会消耗资源,同时更多的线程对象也会占用更多的线程空间(每个线程需要大约1M的内存)。所以需要手动或者自动(常为cpu核心个数+1)指定线程的个数,减少资源抢占。

缺少复用

一个线程对象的生命周期可以分为以下三个阶段 1. 对象创建,分配资源—T1 2. 对象使用,执行任务—T2 3. 对象销毁,GC回收—T3 对唯一一个需要执行的任务来说,他的总执行时间是T=T1+T2+T3。但是对多个任务,线程只是任务的载体,可以利用现有存活的线程资源执行。T1和T3消耗的时间可以优化掉,最终执行时间T≈T2;

匿名对象,无法进行更多操作

通过匿名类的方式无法得到当前任务执行状态,通过Future接口,可以得到任务是否执行完成,是否已经取消,取消任务。

    public interface Future<V> {        boolean cancel(boolean mayInterruptIfRunning);        boolean isCancelled();        boolean isDone();    }

ThreadPoolExecutor 线程池

构造函数:

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,            long keepAliveTime, TimeUnit unit,            BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,            RejectedExecutionHandler handler) {    }

参数

corePoolSize

the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set

核心线程数,即使限制也不会回收,但是可以使用allowCoreThreadTimeOut配置它被回收

maximumPoolSize

maximumPoolSize the maximum number of threads to allow in the pool

线程池能够容纳的最大线程数,当活动线程数达到这个数值后,后序的新任务将会被阻塞。

keepAliveTime

the maximum number of threads to allow in the pool keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

非核心线程的等待超时时长,非核心线程限制超过这个时长会被回收。

unit

unit the time unit for the keepAliveTime argumentkeepAliveTime

时间单位

        // 常用单位毫秒、秒、小时         TimeUnit.MILLISECONDS         TimeUnit.SECONDS         TimeUnit.HOURS

workQueue

the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method

线程池的任务队列,只有Runnable类型的对象才会 提交到这里面。

threadFactory

threadFactory the factory to use when the executor creates a new threadthreadFactory是一个接口
    public interface ThreadFactory {        Thread newThread(Runnable r);    }
接口只实现一个方法,创建一个线程,`threadFactory`就是专为线程池创建线程。

handler

the handler to use when execution is blocked because the thread bounds and queue capacities are reached。

RejectedExecutionHandler提供一个异常处理机制

用法

    private static final int CPU_COUNT = Runtime.getRuntime()            .availableProcessors();    private static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;    private static final int KEEP_ALIVE = 1;    private static final ThreadFactory mThreadFactory = new ThreadFactory() {        // 一种通过线程安全的Integer        private final AtomicInteger mCount = new AtomicInteger(1);        @Override        public Thread newThread(Runnable r) {            return new Thread(r, "Task:" + mCount.getAndIncrement());        }    };    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(            128);    public static final Executor threadPools = new ThreadPoolExecutor(            CPU_COUNT + 1, MAX_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,            sPoolWorkQueue);
  1. 核心线程为CPU核心数+1
  2. 最大线程为CPU核心数*2+1
  3. 核心线程长期存活,非核心线程等待时间为1s
  4. 任务队列为128

执行流程

这里写图片描述
任务执行流程:
1. 如果线程池中核心线程数未满,直接开启一个核心线程
2. 如果核心线程已满,就将任务展存在执行队列,队列可以配置执行顺序(FIFO,LIFO)
3. 如果workQueue也满了,但是最大线程数未满,直接开启一个非核心线程执行任务,非核心线程有超时时间,闲置超过超时时间就回收。
4. 如果线程池中的存活线程数量已经超过了,最大线程数,抛出异常。

线程池分类-Executors工具类

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,完全不创建非核心线程。

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

newCachedThreadPool

一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。没有核心线程,有一个近乎于无限大的线程池,可以创建非常多的非核心线程。特别适合执行大量的消耗时间少的任务。

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

newScheduledThreadPool—ScheduledThreadPoolExecutor

一个定长线程池,支持定时及周期性任务执行。

    public ScheduledThreadPoolExecutor(int corePoolSize) {        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,              new DelayedWorkQueue());    }

使用方法,如下是ScheduledThreadPoolExecutor的成员方法

public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

delay-当前任务延迟执行时间

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay, long period,TimeUnit unit);

initialDelay—第一个任务延迟执行时间
period—两次任务执行之间的时间间隔也就是可以使用这个封装实现,每隔一段时间执行一个任务,使用它可以实现推送服务的心跳连接。

newSingleThreadExecutor

线程池内部只有一个核心线程,确保所有任务都在同一个线程中按顺序执行。这样的话统一所有的外界任务到同一个线程,任务之间不需要处理线程同步的问题。

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

线程可控—Callable、Future、FutureTask

回到开始说的那个问题:匿名对象,无法进行更多操作,通过匿名类的方式无法得到当前任务执行状态,通过Future接口,可以得到任务是否执行完成,是否已经取消,取消任务。要解决这个问题,在线程池里创建了三个类型:

Callable

通过上面介绍的线程池,得到一个ExecutorService

public interface ExecutorService extends Executor {    <T> Future<T> submit(Callable<T> task);    <T> Future<T> submit(Runnable task, T result);    Future<?> submit(Runnable task);}

继承一个Executor接口

public interface Executor {        void execute(Runnable command);}

发现Callable和Runnable都是ExecutorService实例化对象支持的类型,但是Callable包含一个泛型,同时会返回一个Future对象参看Callable实现

public interface Callable<V> {    /**     * Computes a result, or throws an exception if unable to do so.     *     * @return computed result     * @throws Exception if unable to compute a result     */    V call() throws Exception;}
public interface Runnable {    /**     * Starts executing the active part of the class' code. This method is     * called when a thread is started that has been created with a class which     * implements {@code Runnable}.     */    public void run();}

发现RunnableCallable<V>极其相似,只是后者是一个带泛型,事实上这两个接口再使用上也是很相似的,泛型里面携带的其实是任务的执行结果。

Future

查看实现代码:

public interface Future<V> {        boolean cancel(boolean mayInterruptIfRunning);        boolean isCancelled();        boolean isDone();        V get() throws InterruptedException, ExecutionException;        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;}
  • cancel() 方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
  • 参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。
  • isCancelled() 方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  • isDone() 方法表示任务是否已经完成,若任务完成,则返回true;
  • get() 方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。也就是说Future提供三种功能
    1. 获取当前任务执行状态
    2. 判断任务是否执行完成
    3. 得到任务执行结果

FutureaTask

因为Future是个接口,所以需要一个具体的实现—FutureTask

public class FutureTask<V> implements RunnableFuture<V> {}public interface RunnableFuture<V> extends Runnable, Future<V> {    /**     * Sets this Future to the result of its computation     * unless it has been cancelled.     */    void run();}

FutureTask同时实现了Runnable和Future,对比刚才的Callable+返回Futrue的实现形式,这个类型其实就是Callable+Future的组合实现。取消一个任务:

    void test() {        ExecutorService pool = Executors.newFixedThreadPool(2);        Callable<String> s = new Callable<String>() {            @Override            public String call() throws Exception {                System.out.println("test");                return "true";            }        };        Future<String> f = pool.submit(s);        System.out.println(f.isCancelled());        System.out.println(f.isDone());        f.cancel(true);    }

获取执行结果:

    private void init() {        ExecutorService executor = Executors.newCachedThreadPool();        Task task = new Task();        Future<Integer> result = executor.submit(task);        executor.shutdown();        try {            Thread.sleep(1000);        } catch (InterruptedException e1) {            e1.printStackTrace();        }        System.out.println("主线程在执行任务");        try {            System.out.println("task运行结果" + result.get());        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }        System.out.println("所有任务执行完毕");    }    class Task implements Callable<Integer> {        @Override        public Integer call() throws Exception {            System.out.println("子线程在进行计算");            Thread.sleep(3000);            int sum = 0;            for (int i = 0; i < 100; i++)                sum += i;            return sum;        }    }
1 0
原创粉丝点击