美文网首页
线程和线程池

线程和线程池

作者: 玄策 | 来源:发表于2017-08-04 17:26 被阅读6次

    参考资料

    Java多线程问题总结


    目录

    • 1)主线程和子线程
    • 2)线程形态
      • 2.1)AsyncTask
      • 2.2)HandlerThread
      • 2.3)IntentService
    • 3)线程池
      • 3.1)ThreadPoolExecutor
      • 3.2)线程池的分类

    1)主线程和子线程


    2)线程形态

    2.1)AsyncTask

    封装了线程池和Handler,主要方便在子线程中更新UI。

    //Params 参数类型
    //Progress 后台任务执行进度的类型
    //Result 后台任务返回结果的类型
    public abstract class AsyncTask<Params,Progress,Result>
    
    //四个方法
    //在主线程中执行,异步任务执行前,此方法被调用
    onPreExecute()
    
    //线程池中执行
    doInBackground(Params...params){
      //更新进度,会调用onProgressUpdate()
      publicProgress(values);
    }
    
    //异步任务取消时被调用
    onCancelled() 
    
    //主线程中执行,执行进度发生改变调用
    onProgressUpdate(Progress...values)
    
    //主线程中执行,异步任务结束后会调用
    onPostExecute(Result result)
    

    注意:

    • AsyncTask的实例必须在UI thread中创建;
    • execute方法必须在UI thread中调用;
    • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
    • 一个AsyncTask只能被执行一次,即execute()只能被调用一次,否则多次调用时将会出现异常;
    //AsyncTask从Android3.0开始,默认情况下是串行执行的。为了让其在Android3.0及以上版本并行执行,可以采用
    new mAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
    

    AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),
    SerialExecutor用于任务排队,而THREAD_POOL_EXECUTOR用于执行任务,InternalHandler则是将执行环境从线程池切换到主线程。


    2.2)HandlerThread

    HandlerThread继承了Thread,是一种可以使用Handler的Thread,其实它就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。它有个具体使用场景,就是IntentService。
    示例1:

    // Step 1: 创建并启动HandlerThread线程,内部包含Looper
    HandlerThread handlerThread = new HandlerThread("tgf");
    handlerThread.start();
    
    // Step 2: 创建Handler
    Handler handler = new Handler(handlerThread.getLooper());
    
    // Step 3: 发送消息
    handler.post(new Runnable() {
    
            @Override
            public void run() {
                System.out.println("thread id="+Thread.currentThread().getId());
            }
        });
    

    示例2:

    class LooperThread extends Thread {
        public Handler mHandler;
    
        public void run() {
            Looper.prepare();
            // Step 1: 创建Handler
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    //TODO 处理即将发送过来的消息
                }
            };
    
            Looper.loop();
        }
    }
    
    // Step 2: 创建并启动LooperThread线程,内部包含Looper
    LooperThread looperThread = new LooperThread("tgf");
    looperThread.start();
    
    // Step 3: 发送消息
    looperThread.mHandler.sendEmptyMessage(10);
    

    2.3)IntentService

    https://mp.weixin.qq.com/s?__biz=MzI0MjE3OTYwMg==&mid=401611665&idx=1&sn=9b6b1f2924d4adfe4e89a322ab53df9c&scene=21#wechat_redirect
    IntentService是一个服务,内部采用HandlerThread来执行任务,HandlerThread是一个工作线程(子线程),其Handler的handleMessage会回调onHandleIntent并stopSelf,执行完毕自动退出。很像是一个后台线程,但却是一个服务,所以不容易被系统杀死。

    • 多次startService(Intent intent)会串行执行onHandleIntent()
    • 只能startService启动,源码中onBind => return null;
    @override
    protected void onHandleIntent(Intent intent)
    

    3)线程池

    线程是操作系统调度的最小单元,它的创建和销毁都有相应的开销,当系统中存在大量线程时,系统会通过时间片轮转的方式调度。频繁的创建和销毁线程并不高效。正确的方式是采用线程池,一个线程池中缓存一定数量线程,减小开销。

    线程池优点
    避免重复创建销毁的开销
    控制最大并发,避免大量线程争抢资源
    有效的管理,如可以定时或指定间隔循环执行等

    Java的线程池来源于Executor接口,真正实现为ThreadPoolExecutor

    3.1)ThreadPoolExecutor

    //corePoolSize 核心线程数,默认核心线程会在线程池内一直存活,除非设定allowCoreThreadTimeOut=true。
    //maximumPoolSize 最大线程数,活动线程达到此峰值,后续任务将被阻塞
    //keepAliveTime 非核心线程超时时间,超过则被回收
    //unit keepAliveTime的时间单位,枚举TimeUnit.MILLSECONDS(毫秒), TimeUnit.SECONDS(秒)等。
    //workQueue 任务队列,提交的任务runnable对象被存储在这
    //threadFactory 线程工厂,为线程池创建新线程。
    public ThreadPoolExecutor(
      int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue,
      ThreadFactory threadFactory
    )
    
    ThreadPoolExecutor执行任务的规则
    当前线程数量<核心线程数,启动一个核心线程执行任务
    当前线程数量>=核心线程数,将任务插入任务队列排队
    任务队列满了,当前线程数<最大线程数,启动非核心线程执行任务
    任务队列满了,当前线程数>=最大线程数,拒绝此任务

    3.2)线程池的分类

    分类 说明
    FixedThreadPool 只有数量固定核心线程,无超时。所以能快速响应
    CachedThreadPool 只有非核心线程,最大数量Integer.MAX_VALUE,空闲60秒后回收,适合大数量但耗时少任务
    ScheduledThreadPool 核心线程数量固定,非核心线程数无限制,非核心闲置会立即回收,适合定时任务或固定周期重复任务
    SingleThreadPool 只有一个核心线程,适合处理线程同步
    • FixedThreadPool
    //调用
    ExecutorService a = Executor.newFixedThreadPool(4);
    a.execute(runnable);
    
    //源码
    public static ExecutorService newFixedThreadPool(int nThreads){
      return new ThreadPoolExecutor(
        nThreads,
        nThreads,
        0L,
        TimeUnit.MILLSECONDS,
        new LinkedBlockingQueue<Runnable>()
      );
    }
    

    *CachedThreadPool

    ExecutorService a = Executor.newCachedThreadPool();
    a.execute(runnable);
    
    public static ExecutorService newCachedThreadPool(){
      return new ThreadExecutor(
        0,
        Integer.MAX_VALUE,
        60L,
        TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>()
      );
    } 
    
    • ScheduledThreadPool
    ScheduledExecutorService a = Executor.newScheduledThreadPool(4);
    //2000ms后执行runnable
    a.schedule(runnable,2000,TimeUnit.MILLSECONDS);
    //延迟10ms后,每隔1000ms执行一次runnable
    a.scheduleAtFixedRate(runnable,10,1000,TimeUnit.MILLSECONDS);
    
    public static ScheduledExecutor newScheduledThreadPool(int corePoolSize){
      return new ScheduledThreadPoolExecutor(corePoolSize);
    } 
    
    public ScheduledThreadPoolExecutor(int corePoolSize){
      super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,new DelayedWorkQueue());
    }
    
    • SingleThreadPool
    ExecutorService a = Executor.newSingleThreadExecutor();
    a.execute(runnable);
    
    public static ExecutorService newSingleThreadExecutor(){
      return new FinalizableDelegatedExecutorService(
        new ThreadPoolExecutor(
          1,1,0L,TimeUnit.MILLSECONDS,
          new LinkedBlockingQueue<Runnable>()
        )
      );
    } 
    

    相关文章

      网友评论

          本文标题:线程和线程池

          本文链接:https://www.haomeiwen.com/subject/yhovlxtx.html