如转载请标明出处:https://blog.csdn.net/dxh040431104/article/details/93329349
Future, FutureTask,Callable网上的说明很多,我这里就不说了,只说一些我的见解
FutureTask是一个实现了Future的Runable,所以它可以直接丢到Thread中使用如
Callable callable = new Callable() {
@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 callable = new Callable() {
@Override
public Integer call() throws Exception {
return null;
}
} ;
ExecutorService executorService = Executors.newCachedThreadPool();
FutureTask futureTask = new FutureTask(callable);
Future result = executorService.submit(futureTask);
或者
Callable callable = new Callable() {
@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 {
@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 execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask 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 mTasks = new ArrayDeque();
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() {
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(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 implements Callable {
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 {
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 mTasks = new ArrayDeque();
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);
}
}
如果有什么不对的地方请大家指出谢谢