• 关于Android线程和线程池的那些事


    线程相关

    目前常用的线程有:

    • Thread 最基础线程执行方式
    • AsyncTask 封装了线程池和Handler,为了方便开发者在子进程中更新UI
    • HandlerThread
    • IntentService 内部采用HandlerThread执行任务,本质是一个Service,但是表现的更像Thread。因为Thread在后台容易被杀死,所以常用来在后台运行。

    AsyncTask

    使用方法

    1. class MyAsyncTask extends AsyncTask<URL,Integer,Long>{
    2. /**
    3. * 执行异步任务
    4. * @param urls
    5. * @return
    6. */
    7. @Override
    8. protected Long doInBackground(URL... urls) {
    9. int count=100;
    10. long totalSize=0;
    11. for (int i=0;i<count;i++){
    12. totalSize+=10;
    13. publishProgress(i*100/count); //调用onProgressUpdate
    14. try {
    15. Thread.sleep(100);
    16. } catch (InterruptedException e) {
    17. e.printStackTrace();
    18. }
    19. if (isCancelled()){
    20. break;
    21. }
    22. }
    23. return totalSize;
    24. }
    25. /**
    26. * 执行之前调用
    27. */
    28. @Override
    29. protected void onPreExecute() {
    30. super.onPreExecute();
    31. }
    32. /**
    33. * 完成时调用
    34. * @param aLong
    35. */
    36. @Override
    37. protected void onPostExecute(Long aLong) {
    38. super.onPostExecute(aLong);
    39. tv_display.setText("Download"+aLong+"bytes");
    40. }
    41. /**
    42. * 后台进度发生改变时调用
    43. * @param values
    44. */
    45. @Override
    46. protected void onProgressUpdate(Integer... values) {
    47. super.onProgressUpdate(values);
    48. tv_display.setText("进度"+values[0]);
    49. }
    50. }
      new  MyAsyncTask().execute();

    注意事项

    1. AsyncTask 必须在主线程创建
    2. 必须在主线程执行,也就是调用excute();
    3. 不要手动调用doInBackground,onPreExecute,onPostExecute,onProgressUpdate
    4. 每个AsyncTask对象仅能执行一次
    5. AsyncTask在android3.0 以上是串行执行,在android 3.0以下是并行执行。表现状态为用同一个AsyncTask类创建多个AsyncTask对象,并同时调用excute。android 3.0以上会在一个任务执行完成以后,执行另外一个任务。而早期版本会同时执行。
    6. 如果希望android3.0以上,AsyncTask也能并发执行,那么需要调用executeOnExecutor用来替代excute

    HandlerThread

    源码

    1. @Override
    2. public void run() {
    3. mTid = Process.myTid();
    4. Looper.prepare();
    5. synchronized (this) {
    6. mLooper = Looper.myLooper();
    7. notifyAll();
    8. }
    9. Process.setThreadPriority(mPriority);
    10. onLooperPrepared();
    11. Looper.loop();
    12. mTid = -1;
    13. }

    可以看到其内部的run方法,直接新建了Looper。这样就能在非UI线程中,创建Handler了。
    这个方法一般配合IntentService使用,

    注意事项

    • Handler的run 方法是个死循环,在不使用的时候,需要手动调用quit,或者quitSafely来结束线程

    IntentService

    使用方法

    1. public class MyIntentService extends IntentService {
    2. private static final String TAG = "MyIntentService";
    3. public MyIntentService() {
    4. this(null);
    5. }
    6. /**
    7. * Creates an IntentService. Invoked by your subclass's constructor.
    8. *
    9. * @param name Used to name the worker thread, important only for debugging.
    10. */
    11. public MyIntentService(String name) {
    12. super(name);
    13. }
    14. @Override
    15. protected void onHandleIntent(@Nullable Intent intent) {
    16. Log.d(TAG, "onHandleIntent: "+intent.getStringExtra("action"));
    17. SystemClock.sleep(3000);
    18. }
    19. @Override
    20. public void onDestroy() {
    21. super.onDestroy();
    22. Log.d(TAG, "onDestroy: MyIntentService");
    23. }
    24. }
    1. Intent intent=new Intent(ThreadTestActivity.this,MyIntentService.class);
    2. for (int i=0;i<5;i++){
    3. intent.putExtra("action","action"+i);
    4. startService(intent);
    5. }

    注意:虽然IntentService很像thread ,但是本质仍然是service,需要在androidManifest中进行注册。

    源码

    1. @Override
    2. public void onCreate() {
    3. // TODO: It would be nice to have an option to hold a partial wakelock
    4. // during processing, and to have a static startService(Context, Intent)
    5. // method that would launch the service & hand off a wakelock.
    6. super.onCreate();
    7. HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    8. thread.start();
    9. mServiceLooper = thread.getLooper();
    10. mServiceHandler = new ServiceHandler(mServiceLooper);
    11. }

    可以看出IntentService在创建的时候,就创建了HandlerThread,然后通过他的Looper创建了Handler。

    而在IntentService每次被startServcie的时候,都会调用onStartCommand,然后startCommand又会去调用onstart方法,所以我们来看下onstart方法。

    1. @Override
    2. public void onStart(@Nullable Intent intent, int startId) {
    3. Message msg = mServiceHandler.obtainMessage();
    4. msg.arg1 = startId;
    5. msg.obj = intent;
    6. mServiceHandler.sendMessage(msg);
    7. }

    可以看出,在onstart方法中,intentService 将startSevice的intent和startId。放入了handler中。既让放入了handler中,那么我们就需要去看下handlerMessage方法

    1. private final class ServiceHandler extends Handler {
    2. public ServiceHandler(Looper looper) {
    3. super(looper);
    4. }
    5. @Override
    6. public void handleMessage(Message msg) {
    7. onHandleIntent((Intent)msg.obj);
    8. stopSelf(msg.arg1);
    9. }
    10. }

    这个就很简单了,在handler中如果收到消息,则调用抽象方法onHandlerIntent 去处理,而onHandlerIntent ,就是我们需要实现的方法。也就实现了,在非UI线程执行某个操作。

    而最后的stopSerlf,传入的是startId。这个方法会对比现在传入的startId和IntentrService收到的最后第一个startId是不是一样的。也就是当前处理的消息是不是最后一条消息,如果是最后一条消息,则停止该service。

    注意: 由于IntentService内部是由handler实现的,所以也具有Handler的特点。也就是串行执行的,一个方法执行完成以后,才会执行下一个方法。

    线程池相关

    android中常用的线程池

    • ThreadPoolExecutor
    • FixedThreadPool
    • CachedThreadPool
    • ScheduledThreadPool
    • SingleThreadExecutor
    • 其中ThreadPoolExecutor可以算作是线程池接口Executor最基础的实现。后面几个都是对于ThreadPoolExecutor的封装

    ThreadPoolExecutor

    构造方法

    1. public ThreadPoolExecutor(int corePoolSize,
    2. int maximumPoolSize,
    3. long keepAliveTime,
    4. TimeUnit unit,
    5. BlockingQueue<Runnable> workQueue,
    6. ThreadFactory threadFactory) {
    7. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    8. threadFactory, defaultHandler);
    9. }

    corePoolSize

    核心线程的数量,默认一直存活。可以通过allowCoreThreadTimeOut和keepAliveTime配置是否闲置超时关闭和超时的时间

    maximumPoolSize

    线程池最大的线程数,超出将阻塞

    keepAliveTime

    配置非核心线程超时关闭的时间。通过allowCoreThreadTimeOut也可以配置核心线程超时关闭的时间

    unit

    超时的单位,枚举值。

    workQueue

    存储任务的队列

    threadFactory

    线程工厂,提供创建新进程的功能

    FixedThreadPool

    核心代码

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

    从上面代码可以看出,是创建了一个具有以下特点的线程池

    • 核心线程一直存活,
    • 线程数量固定的线程池。
    • 只包含核心线程。
    • 队列没有大小限制。

    该线程使用场景:适合数量较少耗时较长的任务

    CachedThreadPool

    核心代码

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

    从上面代码可以看出,该方法创建了一个具有以下特点的线程池

    • 没有核心线程
    • 线程数量几乎没有限制的
    • 超时时间为60秒
    • 所有的任务都会立即执行

    该线程使用场景:适合数量大耗时较少的任务

    ScheduledThreadPool

    核心代码

    1. public ScheduledThreadPoolExecutor(int corePoolSize) {
    2. super(corePoolSize, Integer.MAX_VALUE,
    3. DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
    4. new DelayedWorkQueue());
    5. }

    该线程池的特点

    • 核心线程固定
    • 非核心线程几乎无限大
    • 非核心线程超时的时候,会立马回收

    该线程使用场景:执行定时和具有周期性的任务

    SingleThreadExecutor

    核心代码

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

    该线程池的特点

    • 只有一个核心线程
    • 没有非核心线程
    • 可以确保所有任务在一个线程中都能按顺序执行

    该线程使用场景:可以统一所有外界任务到一个线程中,任务之间不需要处理线程同步的问题。

  • 相关阅读:
    GIS开发入坑(二)--ArcGIS影像切片并使用GeoServer发布
    Go中 net/http 使用
    Android 9.0 静默安装
    Python 图形化界面基础篇:监听按钮点击事件
    探索数字化节能降碳 广域铭岛助力电解铝行业碳达峰
    python魔法__dir__和__dict__
    数字IC设计面试题目集21~46
    Python中的Numpy向量计算(R与Python系列第三篇)
    新华三路由器+华为交换机,实现华为交换机指定端口访问外网
    后厂村路灯:【干货分享】AppleDevelop苹果开发者到期重置
  • 原文地址:https://blog.csdn.net/cqn2bd2b/article/details/125398732