关于android 线程

作者: DaZenD | 来源:发表于2021-04-26 20:23 被阅读0次

    thread

    线程几种状态

    创建(new) Thread thread=new Thread;

    就绪(runnable) thread.start();

    运行(running) 线程只能从就绪状态进入到运行状态

    阻塞(blocked) 该状态是线程因为某种原因放弃了cpu使用权限,暂时停止运行。

    使用线程的几种方法

    • 继承Thread
    
    private class MyThread extends Thread {
        SyncThread(String name) {
            super(name);
        }
        @Override
        public void run() {
           //执行耗时操作
        }
    }
    
    • 实现Runnable接口
    private class MyThread implements Runnable {
            @Override
            public void run() {
                //执行耗时操作
            }
        }
    
    • 匿名内部类创建线程
    new Thread(new Runnable() {
                public void run() {
                   //执行耗时操作
                }
    }).start();
    

    匿名内部类方法,能这样设置优先级

    thread-priority.png

    handler

    问:为什么要用 Handler消息传递机制

    答:多个线程并发更新UI的同时 保证线程安全

    问:怎么实现线程切换的

    答:Handler 对象在哪个线程下构建(Handler的构造函数在哪个线程下调用),那么Handler就会持有这个线程的Looper引用和这个线程的消息队列的引用。因为持有这个线程的消息队列的引用,意味着这个Handler对象可以在任意其他线程给该线程的消息队列添加消息,也意味着Handler的handlerMessage 肯定也是在该线程执行的

    创建handler的时候,也是先要创建looper的,主线程默认有主线程的looper,如果handler在子线程创建的,那么需要先prepare好looper给handler设置的,要不handler无法创建。。所以,looper是线程切换的关键。。

    内存泄露

    当Handler消息队列 还有未处理的消息 / 正在处理消息时,存在引用关系: “未被处理 / 正处理的消息 -> Handler实例 -> 外部类” (message持有target,即handler,handler不释放,外部类无法释放。。。)

    若出现 Handler的生命周期 > 外部类的生命周期 时(即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁时),将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露

    • 存在“未被处理 / 正处理的消息 -> Handler实例 -> 外部类” 的引用关系
    • Handler的生命周期 > 外部类的生命周期
    • 即 Handler消息队列 还有未处理的消息 / 正在处理消息 而 外部类需销毁

    使得上述任1条件不成立 即可

    解决方案1:静态内部类+弱引用

    解决方案2:当外部类结束生命周期时,清空Handler内消息队列

    AsyncTask

    AsyncTask的实现原理 = 2个线程池(任务队列线程池,执行线程池) + Handler

    其中:线程池用于线程调度、复用 & 执行任务;Handler 用于异步通信

    AsyncTask类属于抽象类,即使用时需 实现子类

    MyTask必须在UI线程中创建 mTask.execute()必须在UI线程中调用

    public abstract class AsyncTask<Params, Progress, Result> { 
     ... 
    }
    
    // 类中参数为3种泛型类型
    // 整体作用:控制AsyncTask子类执行线程任务时各个阶段的返回类型
    // 具体说明:
        // a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
        // b. Progress:异步任务执行过程中,返回下载进度值的类型
        // c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
    // 注:
        // a. 使用时并不是所有类型都被使用
        // b. 若无被使用,可用java.lang.Void类型代替
        // c. 若有不同业务,需额外再写1个AsyncTask的子类
    }
    
    /**
      * 步骤1:创建AsyncTask子类
      * 注: 
      *   a. 继承AsyncTask类
      *   b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
      *   c. 根据需求,在AsyncTask子类内实现核心方法
      */
    
      private class MyTask extends AsyncTask<Params, Progress, Result> {
    
            ....
    
          // 方法1:onPreExecute()
          // 作用:执行 线程任务前的操作
          // 注:根据需求复写
          @Override
          protected void onPreExecute() {
               ...
            }
    
          // 方法2:doInBackground()
          // 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
          // 注:必须复写,从而自定义线程任务
          @Override
          protected String doInBackground(String... params) {
    
                ...// 自定义的线程任务
    
                // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
                 publishProgress(count);
                  
             }
    
          // 方法3:onProgressUpdate()
          // 作用:在主线程 显示线程任务执行的进度
          // 注:根据需求复写
          @Override
          protected void onProgressUpdate(Integer... progresses) {
                ...
    
            }
    
          // 方法4:onPostExecute()
          // 作用:接收线程任务执行结果、将执行结果显示到UI组件
          // 注:必须复写,从而自定义UI操作
          @Override
          protected void onPostExecute(String result) {
    
             ...// UI操作
    
            }
    
          // 方法5:onCancelled()
          // 作用:将异步任务设置为:取消状态
          @Override
            protected void onCancelled() {
            ...
            }
      }
    
    /**
      * 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
      * 注:AsyncTask子类的实例必须在UI线程中创建
      */
      MyTask mTask = new MyTask();
    
    /**
      * 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
      * 注:
      *    a. 必须在UI线程中调用
      *    b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
      *    c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute() 
      *    d. 不能手动调用上述方法
      */
      mTask.execute();
    

    注意:

    • AsyncTask子类的实例必须在UI线程中创建
    • mTask.execute

      • a. 必须在UI线程中调用
      • b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
      • c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
      • d. 不能手动调用上述方法

    关于 生命周期

    • 结论: AsyncTask不与任何组件绑定生命周期
    • 使用建议: 在Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 Fragment的onDestory()调用cancel(boolean);

    关于 内存泄漏

    • 结论 :若AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
    • 使用建议: AsyncTask应被声明为Activity的静态内部类

    线程任务执行结果 丢失

    • 结论: 当Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
    • 使用建议: 在Activity恢复时的对应方法 重启 任务线程

    HandlerThread

    HandlerThread的本质:继承Thread类 & 封装Handler类

    通过继承Thread类,快速地创建1个带有Looper对象的新工作线程

    通过封装Handler类,快速创建Handler & 与其他线程进行通信

    HandlerThread 构造方法可以设置优先级,,创建HandlerThread类对象 = 创建Thread类对象 + 设置线程优先级 = 新开1个工作线程 + 设置线程优先级

    注意: HandlerThread的run方法内才准备looper,所以,外部使用HandlerThread时候,一定要先start开启线程(内部原理:start后开启工作线程,线程的run方法内才创建looper,所以,没法将looper给handler绑定(getLooper方法内wait阻塞了),而是通过持有锁机制先等待looper创建好,然后通知原本getLooper那等着获取looper对象的地方,接着创建handler,接着获取,接着绑定)准备好工作线程

    HandlerThread的使用步骤分为5步

    // 步骤1:创建HandlerThread实例对象
    // 传入参数 = 线程名字,作用 = 标记该线程
       HandlerThread mHandlerThread = new HandlerThread("handlerThread");
    
    // 步骤2:启动线程
       mHandlerThread.start();
    
    // 步骤3:创建工作线程Handler & 复写handleMessage()
    // 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
    // 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
      Handler workHandler = new Handler( handlerThread.getLooper() ) {
                @Override
                public boolean handleMessage(Message msg) {
                    ...//消息处理
                    return true;
                }
            });
    
    // 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
    // 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
      // a. 定义要发送的消息
      Message msg = Message.obtain();
      msg.what = 2; //消息的标识
      msg.obj = "B"; // 消息的存放
      // b. 通过Handler发送消息到其绑定的消息队列
      workHandler.sendMessage(msg);
    
    // 步骤5:结束线程,即停止线程的消息循环
      mHandlerThread.quit();
    

    IntentService

    IntentService本质 = Handler + HandlerThread

    步骤1:定义 IntentService的子类
    传入线程名称、复写onHandleIntent()方法

    public class myIntentService extends IntentService {
    
      /** 
        * 在构造函数中传入线程名字
        **/  
        public myIntentService() {
            // 调用父类的构造函数
            // 参数 = 工作线程的名字
            super("myIntentService");
        }
    
       /** 
         * 复写onHandleIntent()方法
         * 根据 Intent实现 耗时任务 操作
         **/  
        @Override
        protected void onHandleIntent(Intent intent) {
    
            // 根据 Intent的不同,进行不同的事务处理
            String taskName = intent.getExtras().getString("taskName");
            switch (taskName) {
                case "task1":
                    Log.i("myIntentService", "do task1");
                    break;
                case "task2":
                    Log.i("myIntentService", "do task2");
                    break;
                default:
                    break;
            }
        }
    
        @Override
        public void onCreate() {
            Log.i("myIntentService", "onCreate");
            super.onCreate();
        }
       /** 
         * 复写onStartCommand()方法
         * 默认实现 = 将请求的Intent添加到工作队列里
         **/  
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("myIntentService", "onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            Log.i("myIntentService", "onDestroy");
            super.onDestroy();
        }
    }
    

    步骤2:在Manifest.xml中注册服务

    <service android:name=".myIntentService">
                <intent-filter >
                    <action android:name="cn.scu.finch"/>
                </intent-filter>
            </service>
    

    步骤3:在Activity中开启Service服务

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
                // 同一服务只会开启1个工作线程
                // 在onHandleIntent()函数里,依次处理传入的Intent请求
                // 将请求通过Bundle对象传入到Intent,再传入到服务里
    
                // 请求1
                Intent i = new Intent("cn.scu.finch");
                Bundle bundle = new Bundle();
                bundle.putString("taskName", "task1");
                i.putExtras(bundle);
                startService(i);
    
                // 请求2
                Intent i2 = new Intent("cn.scu.finch");
                Bundle bundle2 = new Bundle();
                bundle2.putString("taskName", "task2");
                i2.putExtras(bundle2);
                startService(i2);
    
                startService(i);  //多次启动
            }
        }
    

    源码分析

    IntentService 开启线程:通过onCreate中启动HandlerThread线程,创建ServiceHandler处理通知。

    @Override
    public void onCreate() {
        super.onCreate();
        
        // 1. 通过实例化andlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
        // HandlerThread继承自Thread,内部封装了 Looper
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
      
        // 2. 获得工作线程的 Looper & 维护自己的工作队列
        mServiceLooper = thread.getLooper();
    
        // 3. 新建mServiceHandler & 绑定上述获得Looper
        // 新建的Handler 属于工作线程 ->>分析1
        mServiceHandler = new ServiceHandler(mServiceLooper); 
    }
    
    
       /** 
         * 分析1:ServiceHandler源码分析
         **/ 
         private final class ServiceHandler extends Handler {
    
             // 构造函数
             public ServiceHandler(Looper looper) {
             super(looper);
           }
    
            // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
            @Override
             public void handleMessage(Message msg) {
      
              // onHandleIntent 方法在工作线程中执行
              // onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
              onHandleIntent((Intent)msg.obj);
              // 执行完调用 stopSelf() 结束服务
              stopSelf(msg.arg1);
    
        }
    }
    
       /** 
         * 分析2: onHandleIntent()源码分析
         * onHandleIntent() = 抽象方法,使用时需重写
         **/ 
          @WorkerThread
          protected abstract void onHandleIntent(Intent intent);
    
    /** 
      * onStartCommand()源码分析
      * onHandleIntent() = 抽象方法,使用时需重写
      **/ 
      public int onStartCommand(Intent intent, int flags, int startId) {
    
        // 调用onStart()->>分析1
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    
    /** 
      * 分析1:onStart(intent, startId)
      **/ 
      public void onStart(Intent intent, int startId) {
    
        // 1. 获得ServiceHandler消息的引用
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
    
        // 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
        //这里的Intent  = 启动服务时startService(Intent) 里传入的 Intent
        msg.obj = intent;
    
        // 3. 发送消息,即 添加到消息队列里
        mServiceHandler.sendMessage(msg);
    }
    

    工作任务队列 = 顺序执行

    即 若一个任务正在IntentService中执行,此时你再发送1个新的任务请求,这个新的任务会一直等待直到前面一个任务执行完毕后才开始执行

    静态启动service

    // 在IntentService中,onBind()`默认返回null
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    

    采用 bindService()启动 IntentService的生命周期如下:

    onCreate() ->> onBind() ->> onunbind()->> onDestory()

    即,并不会调用onStart() 或 onStartcommand(),故不会将消息发送到消息队列,那么onHandleIntent()将不会回调,即无法实现多线程的操作,此时,你应该使用Service,而不是IntentService

    从上面源码可看出:IntentService本质 = Handler + HandlerThread:

    • 通过HandlerThread 单独开启1个工作线程:IntentService
    • 创建1个内部 Handler :ServiceHandler
    • 绑定 ServiceHandler 与 IntentService
    • 通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作队列中 & 逐个发送给 onHandleIntent()
    • 通过onHandleIntent() 依次处理所有Intent对象所对应的任务
    • 因此我们通过复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作 即可

    ThreadPool

    https://blog.csdn.net/qq_41648631/article/details/102871630

    线程池的好处:

    重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
    能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
    能对线程进行简单的管理。并提供定时执行以及指定间隔循环执行等功能。

    最基本的构造方法

    public ThreadPoolExecutor(
    //核心线程数,除非allowCoreThreadTimeOut被设置为true,否则它闲着也不会死
    //线程池维护线程的最少数量。线程池至少会保持改数量的线程存在,即使没有任务可以处理。(注意:这里说的至少是指线程达到这个数量后,即使有空闲的线程也不会释放,而不是说线程池创建好之后就会初始化这么多线程)
    int corePoolSize,
    //最大线程数,活动线程数量超过它,后续任务就会排队   
    //线程池维护线程的最大数量。线程池最多可创建的线程数,即使队列中的任务满了线程数量也不会超过maximumPoolSize                      
    int maximumPoolSize,
    //超时时长,作用于非核心线程(allowCoreThreadTimeOut被设置为true时也会同时作用于核心线程),闲置超时便被回收                          
    long keepAliveTime,
    //枚举类型,设置keepAliveTime的单位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等
    TimeUnit unit,
    //缓冲任务队列,线程池的execute方法会将Runnable对象存储起来
    //ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,PriorityBlockingQueue
    BlockingQueue<Runnable> workQueue,
    //线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程
    ThreadFactory threadFactory,
    //饱和策略,线程池对拒绝任务的处理策略。AbortPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy,自定义
    RejectedExecutionHandler handler) {
    

    线程池工作原理

    execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。
    execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行。
    execute一个线程之后,如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。
    execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。

    总的来说:

    1、maximumPoolSize就是线程池最大线程对象的容量 > 核心 + 非核心

    2、就任务而言,先紧着核心线程,满了后,在核心现场的等待队列排队,再满了,说明任务很多啊,来的又太快,只能继续开非核心线程

    3、任务较多,要用到阻塞策略,任务太多,要用到饱和策略

    阻塞

    缓存队列,就是阻塞队列,就是核心线程不够用时候的一个缓存,分几种,BlockingQueue类型的

    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。FIFO,需要指定队列大小,如果队列满了,会触发线程池的RejectedExecutionHandler逻辑
    
    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。FIFO,可以无限向队列中添加任务,直到内存溢出
    
    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。优先级队列,线程池会优先选取优先级高的任务执行,队列中的元素必须实现Comparable接口
    
    DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    
    SynchronousQueue:一个不存储元素的阻塞队列。一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。可以简单理解为是一个容量只有1的队列。Executors.newCachedThreadPool使用的是这个队列
    
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
    

    饱和策略

    也就是线程数超过maximumPoolSize,会抛异常
    当任务队列和线程池都满了时所采取的应对策略,默认 是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。

    AbortPolicy:    线程池默认的策略,如果元素添加到线程池失败,会抛出RejectedExecutionException异常
    DiscardPolicy:  如果添加失败,则放弃,并且不会抛出任何异常
    DiscardOldestPolicy:    如果添加到线程池失败,会将队列中最早添加的元素移除,再尝试添加,如果失败则按该策略不断重试
    CallerRunsPolicy:   如果添加失败,那么主线程会自己调用执行器中的execute方法来执行改任务
    自定义:    如果觉得以上几种策略都不合适,那么可以自定义符合场景的拒绝策略。需要实现RejectedExecutionHandler接口,并将自己的逻辑写在rejectedExecution方法内。
    

    线程池分类

    1、newCachedThreadPool:

    底层:返回ThreadPoolExecutor实例,corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)
    通俗:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
    适用:执行很多短期异步的小程序或者负载较轻的服务器
    2、newFixedThreadPool:

    底层:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue() 无解阻塞队列
    通俗:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不再添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
    适用:执行长期的任务,性能好很多
    3、newSingleThreadExecutor:

    底层:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue() 无解阻塞队列
    通俗:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
    适用:一个任务一个任务执行的场景
    4、NewScheduledThreadPool:

    底层:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列
    通俗:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
    适用:周期性执行任务的场景

    自定义阻塞,按优先级
    public abstract class PriorityRunnable implements Runnable,Comparable<PriorityRunnable> {
        private int priority;
    
        public  PriorityRunnable(int priority){
            if(priority <0) {
                throw new IllegalArgumentException();
            }
            this.priority = priority;
        }
    
        public int getPriority() {
            return priority;
        }
    
        @Override
        public int compareTo(@NonNull PriorityRunnable another) {
            int me = this.priority;
            int anotherPri=another.getPriority();
            return me == anotherPri ? 0 : me < anotherPri ? 1 : -1;
        }
    
        @Override
        public void run() {
                doSomeThing();
        }
    
        protected abstract void doSomeThing();
    }
    
    自定义饱和

    如果以上策略都不符合业务场景,那么可以自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法里去定义就OK了。
    例如:我定义了我的一个拒绝策略,叫做MyRejectPolicy,里面的逻辑就是打印处理被拒绝的任务内容

    public class MyRejectPolicy implements RejectedExecutionHandler{
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //Sender是我的Runnable类,里面有message字段
            if (r instanceof Sender) {
                Sender sender = (Sender) r;
                //直接打印
                System.out.println(sender.getMessage());
            }
        }
    }
    

    参考

    https://blog.csdn.net/qq_41648631/article/details/103045252

    相关文章

      网友评论

        本文标题:关于android 线程

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