Android线程和线程池

本文深入探讨了Android中的线程管理,包括主线程与子线程的区别、AsyncTask的使用及注意事项、HandlerThread的工作原理以及IntentService的应用场景。此外,还介绍了Android中线程池的种类及其特性。

Android中的线程扩展

Android里面的线程我们区分主线程和子线程的两个大类。至于为啥搞两个区分,主要是因为如果在主线程做耗时的事情就会导致程序无法及时的响应。Android中的线程的扩展类有很多,除了Thread类之外,例如有HandlerThread,然后有依赖HandlerThread实现的IntentService。他们两本质上面还是线程Thread。另外AsyncTask使用了线程池来实现,并且封装了Handler的,这样方便开发者可以在子线程中发送消息更新UI。

AsyncTask

AsyncTask基础

AsyncTask是一个轻量级的异步任务类,它在线程池中执行后台任务,然后把结果和进度可以传递给主线程。AsyncTask类的声明如下:

public abstract class AsyncTask<Params, Progress, Result> 

我们看到它是一个抽象类,我们继承这个类实现它的方法即可以使用,它的泛型参数Params为我们调用它可以传递的参数,Progress为在更新的时候可以传递的进度,Result为执行完毕后的结果类。
使用示例:

//点击按钮停止执行
    public void doIt(View view) {
        if(!mAsyncTask.isCancelled()){
            mAsyncTask.cancel(true);
        }
    }

    private class MyAsyncTask extends AsyncTask<Integer, Integer, Void> {
        private Integer total = 0;

        //异步任务开始前执行,准备开始执行
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute: ");
        }

        //执行异步任务方法,得到Params参数。
        @Override
        protected Void doInBackground(Integer... params) {
            for (int j = 0; j < params.length; j++) {
                total += params[j];
            }
            for (int i = 0; i <= total; i += 100) {
                SystemClock.sleep(100);
                if(!isCancelled())
                publishProgress(i);
            }
            return null;
        }

        //在主线程调用,进度更新调用函数
        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgressBar.setMax(total);
            mProgressBar.setProgress(values[0]);
        }

        //在主线程执行,得到异步执行的结果。
        @Override
        protected void onPostExecute(Void aVoid) {
            Log.i(TAG, "onPostExecute: ");
        }

        //在主线程调用取消异步任务则onPostExecute方法不会执行
        @Override
        protected void onCancelled() {
            Log.i(TAG, "onCancelled: ");
            super.onCancelled();
        }
    }

AsyncTask使用注意点:

  • AsyncTask类必须要在主线程加载实例化
  • execute方法必须在主线程调用
  • 不要自己通过代码调用onPreExecute(),onPostExecute(),doInBackground()和onProgressUpdate()。
  • 一个AsyncTask对象只能执行一次,即execute方法只能调用一次,否则会报异常如下代码:
switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
  • 在Android1.6之前,AsyncTask是串行执行任务的,Android1.6的时候AsyncTask开始采用线程池来处理并行任务,但是从Android3.0开始,为了避免并行带来的错误,AsyncTask又采用一个线程来串行执行任务。但是在Android3.0以及以后的版本中我们可以通过调用executeOnExecutor方法来并行的执行任务。

验证上面最后一个结论:
测试默认的AsyncTask执行为串行:

public void start(View view) {
        new MyAsyncTask("AsyncTask1").execute();
        new MyAsyncTask("AsyncTask2").execute();
        new MyAsyncTask("AsyncTask3").execute();
        new MyAsyncTask("AsyncTask4").execute();
    }

    class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        private String mName;

        public MyAsyncTask(String name) {
            this.mName = name;
        }

        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(3000);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
            Log.i(TAG, "onPostExecute: " + simpleDateFormat.format(new Date()));
        }
    }

日志如下:


这里写图片描述

我们看到AsyncTask里面的任务是默认串行执行的。如果我们想要任务并发的执行,需要调用替换调用AsyncTask的executeOnExecutor方法,然后使用AsyncTask的任务实际执行的Executor对象THREAD_POOL_EXECUTOR(或者自定义Executor)作为参数传递执行。AsyncTask实现原理部分代码我们可以看到有两个Executor,一个sDefaultExecutor它是把任务加入默认的队列排队的一个Executor,而我们上面的THREAD_POOL_EXECUTOR是实际执行任务的Executor对象。我们修改上述代码如下:

public void start(View view) {
        new MyAsyncTask("AsyncTask1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        new MyAsyncTask("AsyncTask2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        new MyAsyncTask("AsyncTask3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        new MyAsyncTask("AsyncTask4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        new MyAsyncTask("AsyncTask5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        new MyAsyncTask("AsyncTask6").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
    }

查看执行结果:


这里写图片描述

我们把任务并发的执行了,但是最大的并发数目为3个,因为由线程池的CORE_POOL_SIZE来决定的。

这里的AsyncTask源码分析参考链接郭霖大神的

AsyncTask的缺点

  • 生命周期
    Activity中的AsyncTask不会随着Activity的销毁而销毁,它的子线程的doInBackground方法还会执行。这时可以通过AsyncTask的cancel方法来取消它。

  • 内存泄漏
    我们通常在Activity里面定义非静态的AsyncTask类,由于Java内部类的特点,AsyncTask会持有外部类Activity的引用,如果AsyncTask没有结束它的生命周期会大于Activity的生命周期,所以这时会发生内存泄漏。

  • cancel方法可能没有真正的取消
    cancel传递参数false表示doInBackground会执行到结束,只是不会调用onPostExecute方法。所以咋们传递这个参数并没有实际终止线程,那么传递true表示要立即终止线程,但是。。。如果doInBackground方法里面有不可打断的方法的时候会实效,例如BitmapFactory.decodeStream方法,那么我们得捕获这个异常来处理我们得终止逻辑。

  • 使用无用的Activity引用
    当屏幕旋转或者Activity重建时,之前的AsyncTask不会停止,它执行完毕了以后如果要使用Activity的实例那么就是之前无用的那个Activity引用了。

    HandlerThread

    HandlerThread继承自Thread,它是一个可以使用Handler的Thread,我们之前了解Handler的原理也知道,在子线程run方法里面创建Looper构建循环就可以在HandlerThread里面使用Handler了。下面的代码简单的介绍如何使用Handler和HandlerThread进行关联使用。注意我们和HandlerThread关联的代码的回调代码handlerMessage方法是运行在子线程的。

public class HandlerThreadTestActivity extends AppCompatActivity {
    private static final String TAG = "HandlerThreadTestActivity";
    private HandlerThread mHandlerThread;
    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread_test);
        Log.i(TAG, "onCreate: "+Thread.currentThread());
        mHandlerThread = new HandlerThread("thread1");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.i(TAG, "handleMessage: "+Thread.currentThread());
                SystemClock.sleep(1000);
                mHandler.sendEmptyMessage(1);
                return false;
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandlerThread.quit();
    }
}

源码解析参考点击

IntentService

IntentService是一个特殊的Service,它继承自Service并且是一个抽象类,所以我们的必须实现它的字类才能使用它。IntentService可用于执行后台耗时的任务,当任务执行完毕以后会自动停止,它的子线程运行在服务中,所以它的优先级比一般的线程高的多。从IntentService的构造方法中我们知道它封装了HandlerThread和Handler。

public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

当有任务到达IntentService的时候,它会调用onStartCommmand方法,然后调用onStart中的下述代码:

public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

其实很简单,就是往封装的Handler里面发送消息,这个Handler接收消息会掉发生在封装的HandlerThread子线程中,我们看看回调的代码。

private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

这里的回调会调用我们创建IntentService的时候写的抽象方法onHandlerIntent方法,有个stopSelf(int startId)和stopSelf()方法,两者的区别是,没有参数的会立即停止服务,另一个会等待所有的消息都处理完毕以后才终止关闭服务。
如果目前我们只有一个后台任务,那么onHandlerIntent方法执行完这个任务后,stopSelf(int startId)就会直接停止服务。如果有多个后台任务,那么当onHandlerIntent方法执行完最后一个任务时,stopSelf(int startId)才会直接终止服务。这里我们有一个结论:每执行一个任务会启动一次IntentService如果Service没有销毁,那么消息任务会发送到Service的HandlerThread中顺序执行,最后执行完销毁Service。下面验证这个结论。

Service代码:

package com.lyman.threadtest;

import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log;

public class IntentServiceTestService extends IntentService {
    private static final String TAG = "IntentServiceTestService";
    private static final String ACTION_FOO = "com.lyman.threadtest.action.FOO";
    private static final String ACTION_BAZ = "com.lyman.threadtest.action.BAZ";

    private static final String EXTRA_PARAM1 = "com.lyman.threadtest.extra.PARAM1";
    private static final String EXTRA_PARAM2 = "com.lyman.threadtest.extra.PARAM2";

    public IntentServiceTestService() {
        super("IntentServiceTestService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate: ");
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.i(TAG, "onStart: startId==" + startId);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand: startId==" + startId);
        return super.onStartCommand(intent, flags, startId);
    }

    public static void startActionFoo(Context context, String param1, String param2) {
        Intent intent = new Intent(context, IntentServiceTestService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    public static void startActionBaz(Context context, String param1, String param2) {
        Intent intent = new Intent(context, IntentServiceTestService.class);
        intent.setAction(ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionFoo(param1, param2);
            } else if (ACTION_BAZ.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionBaz(param1, param2);
            }
        }
    }

    /**
     * Handle action Foo in the provided background thread with the provided
     * parameters.
     */
    private void handleActionFoo(String param1, String param2) {
        Log.i(TAG, "handleActionFoo: ");
    }

    /**
     * Handle action Baz in the provided background thread with the provided
     * parameters.
     */
    private void handleActionBaz(String param1, String param2) {
        Log.i(TAG, "handleActionBaz: ");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }
}

Activity中在onCreate方法中启动两个Service,Service日志如下:


这里写图片描述

这里我们看到它会等两个任务执行完毕以后再销毁Service。
接下来我们在Activity中不连续执行启动两个任务,等执行完了一个以后再启动另一个任务。
执行日志如下:

这里写图片描述

这样即验证了我们上面的结论:每执行一个任务会启动一次IntentService。如果Service没有销毁,那么消息任务会发送到Service的HandlerThread中顺序执行,最后执行完销毁Service

Android中的线程池

Android中的线程池的概念来源于Java中的Executor,它是一个接口,真正的实现为ThreadPoolExecutor。ThreadPoolExecutor的构造方法提供了一些列参数来配置线程池,通过不同的参数可以创建不同的线程池。从线程池功能上来分为四类,这四类线程池可以通过Executors所提供的工厂方法来得到。那么我们为啥要使用线程池,原因如下:

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

ThreadPoolExecutor

ThreadPoolExecutor是线程池的真正实现,它的构造方法的参数我们要了解它们的含义。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize:线程池核心线程数,默认情况下,核心线程会在线程池一直存活,即使它已经空闲状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待任务到来的时候就会有超时策略,这个时间间隔由keepAliveTime所指定,当超过keepAliveTime所指定时间后,核心线程会被终止。
  • maximumPoolSize:线程池可容纳最大线程数,当达到这个数后,后面的任务就会阻塞。
  • keepAliveTime:非核心线程闲置时的超时时间,超过这个时间,非核心线程就会被回收。当上述的corePoolSize设置了allowCoreThreadTimeOut为true时,同样作用与核心线程。
  • workQueue:线程池的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
  • threadFactory:线程工厂,为线程池提供创建新线程。
  • handler:当线程池无法执行新任务时,可能由于任务队列已满或者无法成功执行任务,会调用handler的rejectedExecution方法来通知调用者。

线程池的分类

ThreadPoolExecutor按照功能来分为四类:FixedThreadPool,CacheThreadPool,ScheduledThreadPool,SingleThreadExecutor。他们可以通过Executors类的静态方法来进行创建。

FixedThreadPool

通过Executors的newFixedThreadPool方法来创建。构造方法如下:

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

它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池关闭了。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它可以更加快速的响应外界的请求。并且核心线程没有超时机制,另外任务队列也没有大小限制。

CachedThreadPool

通过Executors的newCachedThreadPool方法来创建。

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

它是一种线程数量不定的线程池,它只有非核心线程,并且最大线程数为Integer.MAX_VALUE。由于这个数很大,我们可以认为最大线程数可以任意大。当线程池中的线程都处于活动状态时,线程池就会创建新的线程来处理新任务,否则就利用空闲的线程来处理。非核心线程的超时时间为60秒。CachedThreadPool的任务队列为SynchrondosQueue,它其实相当于一个空集合,任何任务插入以后会立即被执行。从CachedThreadPool特效来看。它适合执行大量的耗时较少的任务。

ScheduledThreadPool

通过Executors的newScheduledThreadPool方法来创建。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

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

它的核心线程数量是固定的,而非核心线程是没有限制的,并且当非核心线程闲置时会被立即回收。这种线程池主要用于执行定时任务和具有固定周期的重复任务。

SingleThreadExecutor

通过Executors的newSingleThreadExecutor方法来创建。

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

这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。那么我们如果使用它就不需要处理任务之间的线程同步问题。

下面对每个线程池来进行简单的使用示例:

public class MyClass {
    private static final String TAG = "MyClass";

    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("start:");
                    Thread.sleep(2000);
                    System.out.println("end:");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        ExecutorService fixedService = Executors.newFixedThreadPool(5);
        fixedService.execute(runnable);
        fixedService.shutdown();

        ExecutorService cachedService = Executors.newCachedThreadPool();
        cachedService.execute(runnable);
        cachedService.shutdown();

        ScheduledExecutorService scheduleService = Executors.newScheduledThreadPool(5);
        //延迟两秒执行
        scheduleService.schedule(runnable, 2, TimeUnit.SECONDS);
        scheduleService.shutdown();

        //延迟零秒后每隔两秒重复执行线程
//        scheduleService.scheduleAtFixedRate(runnable, 0, 2, TimeUnit.SECONDS);

        ExecutorService singleService = Executors.newSingleThreadExecutor();
        singleService.execute(runnable);
        singleService.execute(runnable);
        singleService.execute(runnable);
        //任务执行完毕关闭线程池
        singleService.shutdown();
    }
}

详细线程池讲解参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值