Android-线程管理

2017/5/30 posted in  Android

Thread

  • 继承Thread方式
  • 实现Runnable接口

Thread主要函数:

  • run()//包含线程运行时所执行的代码
  • start()//用于启动线程
  • sleep()/sleep(long millis)//线程休眠,交出CPU,让CPU去执行其他的任务,然后线程进入阻塞状态,sleep方法不会释放锁
  • yield()//使当前线程交出CPU,让CPU去执行其他的任务,但不会是线程进入阻塞状态,而是重置为就绪状态,yield方法不会释放锁
  • join()/join(long millis)/join(long millis,int nanoseconds)//等待线程终止,直白的说 就是发起该子线程的线程 只有等待该子线程运行结束才能继续往下运行
  • wait()//交出cpu,让CPU去执行其他的任务,让线程进入阻塞状态,同时也会释放锁
  • interrupt()//中断线程,自stop函数过时之后,我们通过interrupt方法和isInterrupted()方法来停止正在运行的线程,注意只能中断已经处于阻塞的线程
  • getId()//获取当前线程的ID
  • getName()/setName()//获取和设置线程的名字
  • getPriority()/setPriority()//获取和这是线程的优先级 一般property用1-10的整数表示,默认优先级是5,优先级最高是10,优先级高的线程被执行的机率高
  • setDaemon()/isDaemo()//设置和判断是否是守护线程
  • currentThread()//静态函数获取当前线程

Thread线程主要状态

  1. New  一旦被实例化之后就处于new状态
  2. Runnable 调用了start函数之后就处于Runnable状态
  3. Running 线程被cpu执行 调用run函数之后 就处于Running状态
  4. Blocked 调用join()、sleep()、wait()使线程处于Blocked状态
  5. Dead    线程的run()方法运行完毕或被中断或被异常退出,线程将会到达Dead状态

如何停止一个线程

通过interrupt方法和isInterrupted()方法来停止正在运行的线程,首先必须先让线程处于阻塞状态

Thread线程同步问题

  • 同步函数
  • 同步代码块
  • 使用特殊域变量(volatile)实现线程同步

ExecutorService线程池

new Thread()的缺点

  • 每次new Thread()耗费性能
  • 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。
  • 不利于扩展,比如如定时执行、定期执行、线程中断
    #### 采用线程池的优点
  • 重用存在的线程,减少对象创建、消亡的开销,性能佳
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
  • 提供定时执行、定期执行、单线程、并发数控制等功能

ExecutorService

public interface ExecutorService extends Executor{
    void shutdown();//顺次地关闭ExecutorService,停止接收新的任务,等待所有已经提交的任务执行完毕之后,关闭ExecutorService

    List<Runnable> shutdownNow();//阻止等待任务启动并试图停止当前正在执行的任务,停止接收新的任务,返回处于等待的任务列表

    boolean isShutdown();//判断线程池是否已经关闭

    boolean isTerminated();//如果关闭后所有任务都已完成,则返回 true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。

    
    boolean awaitTermination(long timeout, TimeUnit unit)//等待(阻塞)直到关闭或最长等待时间或发生中断,timeout - 最长等待时间 ,unit - timeout 参数的时间单位  如果此执行程序终止,则返回 true;如果终止前超时期满,则返回 false 

 
    <T> Future<T> submit(Callable<T> task);//提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。该 Future 的 get 方法在成功完成时将会返回该任务的结果。

    <T> Future<T> submit(Runnable task, T result);//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功完成时将会返回给定的结果。

 
    Future<?> submit(Runnable task);//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。
        throws InterruptedException;

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。返回列表的所有元素的 Future.isDone() 为 true。
        throws InterruptedException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks)//执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。
        throws InterruptedException, ExecutionException;

    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;   
}

Executor接口

public interface Executor {

    void execute(Runnable command);//执行已提交的 Runnable 任务对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法
}

Executors工厂类

  • newFixedThreadPool()创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。(运行结果:总共只会创建5个线程, 开始执行五个线程,当五个线程都处于活动状态,再次提交的任务都会加入队列等到其他线程运行结束,当线程处于空闲状态时会被下一个任务复用)
  • newCachedThreadPool()创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程(运行结果:可以看出缓存线程池大小是不定值,可以需要创建不同数量的线程,在使用缓存型池时,先查看池中有没有以前创建的线程,如果有,就复用.如果没有,就新建新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务)
  • newScheduledThreadPool()创建一个定长线程池,支持定时及周期性任务执行schedule(Runnable command,long delay, TimeUnit unit)创建并执行在给定延迟后启用的一次性操作(运行结果和newFixedThreadPool类似,不同的是newScheduledThreadPool是延时一定时间之后才执行scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit)  创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推)scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
  • newSingleThreadExecutor()创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行(运行结果:只会创建一个线程,当上一个执行完之后才会执行第二个)通过ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();实现延时的单线程线程池。

ThreadPoolExecutor

ThreadPoolExecutor线程池用于管理线程任务队列、若干个线程。

1.ThreadPoolExecutor构造函数

ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue workQueue,RejectedExecutionHandler handler);
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) ;
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory, RejectedExecutionHandler handler);
  • corePoolSize: 线程池维护线程的最少数量  
  • maximumPoolSize:线程池维护线程的最大数量  
  • keepAliveTime: 线程池维护线程所允许的空闲时间  
  • unit: 线程池维护线程所允许的空闲时间的单位  
  • workQueue: 线程池所使用的缓冲队列  
  • threadFactory:线程池用于创建线程  
  • handler: 线程池对拒绝任务的处理策略

AsyncTask

Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。同时在处理单个后台任务时,AsyncTask的代码量更少并且更加轻量级。
AsyncTask定义了三种泛型类型 Params,Progress和Result。

  • Params 启动任务执行的输入参数,比如HTTP请求的URL。
  • Progress 后台任务执行的百分比。
  • Result 后台执行任务最终返回的结果,比如String。
    使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:
  • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
  • onPostExecute(Result)  相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
    有必要的话你还得重写以下这三个方法,但不是必须的:
  • onProgressUpdate(Progress…)   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
  • onPreExecute()        这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
  • onCancelled()             用户调用取消时,要做的操作
    使用AsyncTask类,以下是几条必须遵守的准则:
  • Task的实例必须在UI thread中创建;
  • execute方法必须在UI thread中调用;
  • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
  • 该task只能被执行一次,否则多次调用时将会出现异常;

参考

Android线程管理