由Future, FutureTask,Callable再到AsyncTask

如转载请标明出处:https://blog.csdn.net/dxh040431104/article/details/93329349

 

Future, FutureTask,Callable网上的说明很多,我这里就不说了,只说一些我的见解

FutureTask是一个实现了Future的Runable,所以它可以直接丢到Thread中使用如

Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return null;
    }
} ;
FutureTask futureTask = new FutureTask(callable);
new Thread(futureTask).start();

FutureTask和Runable的区别在于,FutureTask比Runable多了执行的返回值还一些其他的方法,这些是Future接口定义的,cancel,isCancelled,isDone,get

FutureTask同样也可以和线程池Executor一起使用(如Runable一样)

Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return null;
    }
} ;
ExecutorService executorService = Executors.newCachedThreadPool();
FutureTask futureTask = new FutureTask(callable);
Future result = executorService.submit(futureTask);

或者

Callable<Integer> callable = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return null;
    }
} ;
ExecutorService executorService = Executors.newCachedThreadPool();
Future result = executorService.submit(callable);

从源码中应该能看到executorService.submit(callable)其实最后还是封装成executorService.submit(futureTask)。如下


有了上面的了解我们在讨论一下AsyncTask:

private void testAsyncTask() {
    MyAsyncTask myAsyncTask = new MyAsyncTask();
    myAsyncTask.execute();
}

class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... voids) {
        if(isCancelled()) {
            return null;
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
    }
}

这个是最简单的AsyncTask的写法,但是它内部是怎么运行的呢?从execute看下去。

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
.....................
    onPreExecute();
    exec.execute(mFuture);

    return this;
}

大家看到了onPreExecute了是吧,还有@MainThread这个注解,那这个方法基本上就是用来给你在主线程做一些UI更新的。

下面就是exec.execute(mFuture); 从前面就能看到exec是sDefaultExecutor,那么sDefaultExecutor这个是什么?

通过源码我们会发现这个是一个executor,

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

上面红色的Runable r其实就是mFuture了,exec.execute(mFuture)在这里传进去的。真正运行是在scheduleNext中,下面就是重点了THREAD_POOL_EXECUTOR是什么?

public static final Executor THREAD_POOL_EXECUTOR;

static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

其实就是线程池,和我前面讲的是一样的。那么现在大家应该就很清楚了,AsyncTask其实就是利用线程池和FutureTask来做的异步调用。线程池找到了,那么AsyncTask里面的FutureTask和Callable在哪里呢?

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);
            }
        }
    };
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

在AsyncTask的构造函数中能看到。mWorker就是Callable,mFuture就是FutureTask。

那整个流程就很简单了,先运行onPreExecute然后运行Callable中的doInBackground,最后运行postResultIfNotInvoked。

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

postResultIfNotInvoked里面调用的是postResult。是不是很清晰。


注意点1:

因为AsyncTask是用的FutureTask来进行异步的,所以FutureTask存在的一些坑 AsyncTask也存在。

AsyncTask的cancel方法其实不是完全有效的。我们看如下代码:

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

最后是通过Thread.interrupt进行中断的,但是中断是有限制的,如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法,那么可以被中断掉,而网络通讯,文件读取等阻塞都不能被中断

所以如果这个里面有网络请求,我们是无法通过cancel方法停止掉网络请求的。

所以需要自己调用isInterrupt方法判断是否设置了中断标志,对AsyncTask,来说需要在doInBackground里面调用isCancelled来判断。

@Override
protected Void doInBackground(Void... voids) {
    if(isCancelled()) {
        return null;
    }
    return null;
}

注意点2:

    private void testMultiTask() {
        for (int i = 0; i <5 ; i ++) {
            MyAsyncTask task = new MyAsyncTask("tast" + i);
            task.execute();
        }
    }

    class MyAsyncTask extends AsyncTest<Void, Integer, Void> {
        private String name ;
        public MyAsyncTask(String name) {
            this.name = name ;
        }

        @Override
        protected void onPreExecute() {
            Log.e(TAG,name + " ---> onPreExecute");
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... voids) {
            Log.e(TAG,name + " ---> doInBackground start " + Thread.currentThread().getId());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e(TAG,name + " ---> doInBackground end ");
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            Log.e(TAG,name + " ---> onPostExecute ");
        }
    }

如上面的代码,按照我们的意图该代码应该是并行执行的,但是它确实顺序执行,

07-02 16:17:07.449 21064-21064/com.tiger.demo E/AsycActivity: tast0 ---> onPreExecute
07-02 16:17:07.451 21064-21064/com.tiger.demo E/AsycActivity: tast1 ---> onPreExecute
07-02 16:17:07.451 21064-21064/com.tiger.demo E/AsycActivity: tast2 ---> onPreExecute
07-02 16:17:07.452 21064-21421/com.tiger.demo E/AsycActivity: tast0 ---> doInBackground start 2147
07-02 16:17:12.454 21064-21421/com.tiger.demo E/AsycActivity: tast0 ---> doInBackground end 
07-02 16:17:12.456 21064-21064/com.tiger.demo E/AsycActivity: tast0 ---> onPostExecute 
07-02 16:17:12.457 21064-21449/com.tiger.demo E/AsycActivity: tast1 ---> doInBackground start 2148
07-02 16:17:17.458 21064-21449/com.tiger.demo E/AsycActivity: tast1 ---> doInBackground end 
07-02 16:17:17.460 21064-21064/com.tiger.demo E/AsycActivity: tast1 ---> onPostExecute 
07-02 16:17:17.463 21064-21464/com.tiger.demo E/AsycActivity: tast2 ---> doInBackground start 2149
07-02 16:17:22.464 21064-21464/com.tiger.demo E/AsycActivity: tast2 ---> doInBackground end 
07-02 16:17:22.466 21064-21064/com.tiger.demo E/AsycActivity: tast2 ---> onPostExecute 

为什么呢?

它的线程池和队列都是static,所以无论多少个对象最后都是用的一个线程池执行的。

其次:

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

execute后会调用该类的execute,先将Runnable放入ArrayDeque中,然后判断mActive == null 第一次mActive肯定是空,进入scheduleNext,从mTasks取出数据并赋值给mActive,然后丢入线程池。当下一个execute进来判断mActive == null时,此时mAcive已经不为null,所以不会运行scheduleNext,而是在上一个运行完毕后运行scheduleNext。

                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }

如果要修改成并行该怎么弄,AsyncTask.executeOnExecutor()强制指定AsyncTask使用线程池并发调度任务。

    private ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
    private void testMultiTask() {
        for (int i = 0; i <3 ; i ++) {
            MyAsyncTask task = new MyAsyncTask("tast" + i);
            task.executeOnExecutor(threadPoolExecutor);
        }
    }

如果有什么不对的地方请大家指出谢谢

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值