Android中的线程

Posted 川峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的线程相关的知识,希望对你有一定的参考价值。

本文主要是对android当中的线程相关的知识进行复习和总结。

文章目录

new Thread

缺乏统一管理,无限制创建,可能占用过多系统资源导致死机或oom,不推荐。

AsyncTask

场景:需要知晓任务执行的进度,多个任务串行执行
缺点:生命周期和宿主的生命周期不同步,有可能发生内存泄漏,默认情况所有任务串行执行

class MyAsyncTask extends AsyncTask<String, String, String> 
    private static final String TAG = "MyAsyncTask";

    @Override
    protected String doInBackground(String... params) 
        for (int i = 0; i < 100; i++) 
            publishProgress(params[0] + " === "+ (i * 10));
        
        return params[0];
    

    @Override
    protected void onPostExecute(String result) 
        Log.e(TAG, "result: " + result);
    

    @Override
    protected void onProgressUpdate(String... values) 
        Log.e(TAG, "onProgressUpdate: " + values[0]);
    

测试代码:

 private void testAsyncTask() 
     int count = 100;
     for (int i = 0; i < count; i++) 
         //串行
         new MyAsyncTask().execute("MyAsyncTask[ " +i+ " ]");
         //并行
         new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "MyAsyncTask[ " +i+ " ]");
         final String name = "MyAsyncTask[ " +i+ " ]";
         //串行
         AsyncTask.execute(new Runnable() 
             @Override
             public void run() 
                 for (int i = 0; i < 100; i++) 
                     Log.e("MyAsyncTask", "onProgressUpdate:  " + name + " === "+ (i * 10) );
                 
             
         );
         //并行
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() 
             @Override
             public void run() 
                 for (int j = 0; j < 100; j++) 
                     Log.e("MyAsyncTask", "onProgressUpdate:  " + name + " === "+ (j * 10));
                 
             
         );
     
 

AsyncTask源码解析:

AsyncTask的三个泛型参数:

public abstract class AsyncTask<Params, Progress, Result>
  • Params: doInBackground方法的接受参数类型
  • Progress:onProgressUpdate方法的接受参数类型
  • Result:onPostExecute方法的接受参数类型,以及doInBackground方法的返回参数类型

AsyncTask的五个运行方法:

  • onPreExecute(): 在主线程中执行,在异步任务执行之前被调用,一般用于一些准备工作
  • doInBackground(Params… params) :在子线程运行,用来处理后台任务,params就是execute(Params… params)方法传递的参数
  • onProgressUpdate(Progress… values):在主线程运,在doInBackground中可以调用publishProgress来更新进度
  • onPostExecute(Result result):在主线程运行,后台任务处理完毕调用,并返回doInBackground的结果
  • publishProgress(Progress… values): 在doInBackground中调用,来开启进度更新

注意:

  • AsyncTask的实例必须在UI线程中创建
  • execute必须在UI线程中调用
  • 不能在doInBackGround中更改UI组件信息
  • 一个AsyncTask的实例只能执行一次任务,执行第二次会抛出异常

AsyncTask构造函数

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Looper callbackLooper) 
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() 
            public Result call() throws Exception 
                mTaskInvoked.set(true);
                Result result = null;
                try 
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                 catch (Throwable tr) 
                    mCancelled.set(true);
                    throw tr;
                 finally 
                    postResult(result);
                
                return result;
            
        ;

        mFuture = new FutureTask<Result>(mWorker) 
            @Override
            protected void done() 
                try 
                    postResultIfNotInvoked(get());
                 catch (InterruptedException e) 
                    android.util.Log.w(LOG_TAG, e);
                 catch (ExecutionException e) 
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                 catch (CancellationException e) 
                    postResultIfNotInvoked(null);
                
            
        ;
    

首先会尝试初始化一个主线程mHandler 对象,使用 Looper.getMainLooper().然后创建了两个对象:WorkerRunnableFutureTask,分别赋值为mWorkermFuture。这两个对象初始化都实现了两个回调方法,当用户执行了execute方法的时候在特定情况下会触发这两个对象的回调方法。

这里能够看到:doInBackground是在WorkerRunnablecall方法中被回调执行的

WorkerRunnable:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> 
   Params[] mParams;

它是一个实现了 Callable 接口的抽象类

Callable接口:

public interface Callable<V> 
    V call() throws Exception;

Runnable 是一对兄弟,主要用在线程调用中,区别是 Runnablerun() 方法没有返回值,而 Callablecall() 方法有返回值。Callable需要和ExcutorService结合使用,其中ExecutorService也是一个线程池对象继承自Executor接口,ExecutorService提供的方法:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
  • submit(Callable task),传递一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
  • submit(Runnable task, T result),传递一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
  • submit(Runnable task),传递一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。

FutureTask:

public class FutureTask<V> implements RunnableFuture<V> 

    public FutureTask(Callable<V> callable) 
        if (callable == null) throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    
 
    public FutureTask(Runnable runnable, V result) 
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    

    public boolean isCancelled() 
        return state >= CANCELLED;
    

    public boolean isDone() 
        return state != NEW;
    
    
	public void run() 
		...
	
	
	public boolean cancel(boolean mayInterruptIfRunning) ...
	protected void done()  
	public V get() ...
	public void set(V v) ...

它一个实现 RunnableFuture 接口的类,构造函数可接受 Callable 参数类型,也就是 AsyncTask构造函数中的 WorkerRunnable类型的 mWorker对象,同时它具备run方法以及canceldone和状态判断等方法。

至此,我们能看到: WorkerRunnablecall() 方法是在 FutureTaskrun() 方法中调用的

RunnableFuture

继承 RunnableFuture两个接口,run方法是来自Runnable接口

public interface RunnableFuture<V> extends Runnable, Future<V> 
    void run();

public interface Future<V> 
     //取消任务
    boolean cancel(boolean mayInterruptIfRunning);

    //如果任务完成前被取消,则返回true。
    boolean isCancelled();

    //如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
    boolean isDone();

    //获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
    V get() throws InterruptedException, ExecutionException;

    // 获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,
    //如果阻塞时间超过设定的timeout时间,该方法将返回null。
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; 

Future接口能够中断执行中的任务, 判断任务是否完成获取任务执行完成后的结果(get),说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。

因为 RunnableFuture 同时继承了 RunnableFuture两个接口,所以 FutureTask 既可以被当做Runnable使用也可以被当做Future来使用。而 FutureTaskRunnableFuture 的实现类,那么接下来关键就是FutureTaskrun方法在何时被执行?

AsyncTask的execute()方法:

    public final AsyncTask<Params, Progress, Result> execute(Params... params) 
        return executeOnExecutor(sDefaultExecutor, params);
    

调用了 executeOnExecutor 方法,sDefaultExecutor是一个线程池对象

executeOnExecutor方法:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) 
        if (mStatus != Status.PENDING) 
            switch (mStatus) 
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            
        

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    

这里先调用了 onPreExecute() 方法,然后执行了exec.execute(mFuture) ,也就是为啥onPreExecute() 是在主线程执行的,而 FutureTask 对象是被放入线程池中执行。

并且这里判断状态不等于 PENDING 时,只要状态等于 RUNNING 或者 FINISHED就会抛异常!这也是为啥 一个AsyncTask的实例只能执行一次,第二次就会报错的原因!

SerialExecutor:

 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
 
 private static class SerialExecutor implements Executor 
 		// 双端队列 既可以当成队列使用 也可以当成栈使用
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive; // 当前正在执行的那个 mFutureTask

        public synchronized void execute(final Runnable r) 
        	// offer 向队尾插入元素,成功返回 true
            mTasks.offer(new Runnable() 
                public void run() 
                    try 
                        r.run();
                     finally 
                        scheduleNext();
                    
                
            );
            //判断如果没有Runnable在执行,就调用scheduleNext
            if (mActive == null) 
                scheduleNext();
            
        

        protected synchronized void scheduleNext() 
        	// poll 获取并删除队首元素,失败返回null
            if ((mActive = mTasks.poll()) != null) 
                THREAD_POOL_EXECUTOR.execute(mActive); // 由线程池真正的执行任务
            
        
    

sDefaultExecutor 是一个 SerialExecutor 类型的 全局静态的常量 对象 SERIAL_EXECUTORSerialExecutorExecutor 的实现类,在 execute() 方法中调用了 Runnable 对象的 run 方法,因此我们的 mFuture 对象最终是在这里被调用的。(前面提过 FutureTask既是Runnable的实现也是Future的实现)。

这里 SerialExecutor 实际维护了一个队列,每当 execute() 执行并传入一个 FutureTask对象,这个对象就会被加入到队尾,然后判断当前如果没有正在执行的FutureTask对象,就从队首获取一个FutureTask放入线程池中执行。

而加入队列中的 FutureTask对象也不是直接添加的,而是又包了一层 Runnable 对象,这个包一层又有什么用呢?看它这里使用了 try-finally 保证了scheduleNext()方法必被调用,也就是说当前这个 FutureTaskrun方法被执行完以后,会自动取下一个FutureTask放入线程池中执行。

也就是说当我们new一个AsyncTask任务开始execute时, 并不一定立马执行,要看它里面的任务队列情况,如果它是第一个就会立马执行,如果它前面还有,那么就会排队等待。也就是一个串行的任务队列。

至此,我们能得到一个大体的执行流程:
new AsyncTask() --> new FutureTask() --> AsyncTask#execute() —> SerialExecutor#execute() --> mTasks.offer() --> mTasks.poll() --> scheduleNext() --> THREAD_POOL_EXECUTOR.execute() --> FutureTask#run() -->WorkerRunable#call()–>doInBackground()

postResult方法:
再回来看AsyncTask构造函数中的结果返回

最终调用postResult方法:

    private Result postResult(Result result) 
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    
    
    ....
    
    private Handler getHandler() 
        return mHandler;
    
    
	....
    
    private static Handler getMainHandler() 
        synchronized (AsyncTask.class) 
            if (sHandler == null) 
                sHandler = new InternalHandler(Looper.getMainLooper());
            
            return sHandler;
        
    

这里没什么神秘的了,就是通过Handler发消息,而这里的Handler对象就是构造函数第一行代码初始化的运行在主线程的Handler对象(使用了 Looper.getMainLooper() )。

    private static class InternalHandler extends Handler 
        public InternalHandler(Looper looper) 
            super(looper);
        

        @SuppressWarnings("unchecked", "RawUseOfParameterizedType")
        @Override
        public void handleMessage(Message msg) 
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) 
                case MESSAGE_POST_RESULT:
                    // mTask就是当前的AsyncTask对象
                    result.mTask.finish(result.mData[0]以上是关于Android中的线程的主要内容,如果未能解决你的问题,请参考以下文章

线程之间的内存栅栏/屏障如何与其他线程中的栅栏/屏障交互?

Java多线程_同步工具CyclicBarrier

java中的简单屏障同步

Android-Handler同步屏障

在多线程应用程序中使用屏障的真实示例是啥?

Android Handler 机制 屏障消息(同步屏障)