Android AsyncTask原理以及线程池概念

线程池

概念

thread pool一般被用来解决两个问题:当处理大量的同步task的时候,它能够避免thread不断创建销毁的开销。通过使用thread pool可以限制这些任务所消耗的资源,比如最大线程数,比如最大的消息缓冲池。ThreadPoolExecutor不仅仅是简单的多个thread的集合,它还带有一个消息队列


corePoolSize:线程此中核心线程数  
maxPoolSize:线程此中允许运行的最大运行的线程数
这两个参数其实和threadpool的调度策略密切相关:
如果poolsize小于coresize,那么只要来了一个request,就新创建一个thread执行;
如果poolsize已经大于或等于coresize,那么来了一个request后,如果该queue能够接收request(AsynchronousQueue不能接收任务),那么就放进queue中,等来核心线程执行完毕后再执行;一旦且只有queue满了或者queue不接收任务,那么此时还进来新的任务,就会创建新的thread来立刻执行,此时新添的线程数不能超过maxPoolSize-corePoolSize。否则就会那么很有可能发生reject request的事情。很多人会认为这样的系统不好。但其实,reject request很多时候是个好事,因为当负载大于系统的capacity的时候,如果不reject request,系统会出问题的。)

ThreadFactory
可以通过设置默认的ThreadFactory来改变threadpool如何创建thread,这里一般可以设置新线程的名称

keep-alive time
如果实际执行的线程数大于coresize,那么这些超额的thread(不能操作maxPoolSize-corePoolSize)
运行完之后,过了keep-alive的时间之后,就会被kill掉。;

queue有以下几种实现:
任何一个BlockingQueue都可以做为threadpool中的队列,又可以分为三种:
1. AsynchronousQueue,采用这种queue,任何的task会被直接交到thread手中, queue本身不缓存任何的task,所以如果所有的线程在忙的话,新进入的task是会被拒绝的;
2. LinkedBlockingQueue,链表实现,可以设置这个queue的大小, 如果正在运行的线程数大于corePoolSize,那么就会新的task就会进入这个queue,如果这个queue都插满了的话,然后只要新的task一来,就会创建新的线程立刻执行,如果此时新的task超过了maxPoolSize-corePoolSize,就会发生reject request的事情
3. ArrayBlockingQueue,数组实现,类似LinkedBlockingQueue吧,也可以设置queue大小
4. PriorityBlockingQueue : 优先队列,可以针对任务排序
任务:实现runnable接口

RejectExecutionHandler是针对任务无法处理时的一些自保护处理:
1. Reject 直接抛出Reject exception
2. Discard 直接忽略该runnable,不可取
3. DiscardOldest 丢弃最早入队列的的任务
4. CallsRun 直接让原先的client thread做为worker线程,进行执行

总结一下过程:

整个ThreadPoolExecutor的任务处理有4步操作:


1. 初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行
2. 当提交的任务数超过了corePoolSize,就进入了第二步操作。会将当前的runable提交到一个block queue中
3. 如果queue是个有界队列(AsynchronousQueue可以算是无界队列),当队列满了之后就进入了第三步。如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务
4. 如果第三步救急方案也无法处理了,就会走到第四步执行reject操作。
poolSize:表示当前线程池中正在运行的线程


    // 创建可以容纳3个线程的线程池
    ExecutorService fixedThreadPool = Executors. newFixedThreadPool(3);
     此时说明核心线程数为3,最多并行3个线程(如果只有两个任务,那么只开启两个线程),如果超过了3个,那么就会把task放到LinkedBlockingQueue队列里面
    LinkedBlockingQueue的大小是MAX_VALUE。当某个核心线程run运行完当前task之后,然后LinkedBlockingQueue队列中取出一个task放到刚那个线程中执行,达到复用线程的目的
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
    
    
    // 线程池的大小会根据执行的任务数动态分配
    ExecutorService cachedThreadPool = Executors. newCachedThreadPool();
    此时说明核心线程为0,最大线程为MAX_VALUE。所以只要submit了一个任务,就新建一个线程。。然后这个线程run运行完之后,60s就终止了。。
    SynchronousQueue说明这个队列不能接受任何task
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    // 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
    // 所有的task, 都是串行的,因为只有一个线程。。
    ExecutorService singleThreadPool = Executors. newSingleThreadExecutor();
    
    // 类似于newFixedThreadPool,固定的核心线程数,但提供了定时器的功能
    ScheduledExecutorService scheduledThreadPool = Executors. newScheduledThreadPool(3);

    scheduledThreadPool.schedule(runnable, 10, TimeUnit.SECONDS);//10s之后运行runnable这个task


参考   ThreadPoolExecutor原理及使用


    

测试代码

public class Test {
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask#" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(2);
    //private static final SynchronousQueue<Runnable> sPoolWorkQueue = new SynchronousQueue<Runnable>();

    public static final ExecutorService THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(2, 7, 10, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);

    private static void run(ExecutorService threadPool) {
        for (int i = 1; i < 8; i++) {
            final int taskID = i;
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i < 5; i++) {
                        try {
                            Thread.sleep(1000);// 为了测试出效果,让每次任务执行都需要一定时间
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("第" + taskID + "次任务的第" + i + "次执行");
                    }
                }
            });
        }
        // threadPool.shutdown();// 任务执行完毕,关闭线程池
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建可以容纳3个线程的线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(6);
        // 线程池的大小会根据执行的任务数动态分配
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        // 效果类似于Timer定时器
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

        run(THREAD_POOL_EXECUTOR);
        // run(fixedThreadPool);
        // run(cachedThreadPool);
        // run(singleThreadPool);
        // run(scheduledThreadPool);
        Thread.sleep(10000);
        System.out.println("10s过后");
    }
}

    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(2);
    public static final ExecutorService THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(2, 7, 10, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    核心线程2个,queue可接收2个task,另外如果还有task进来就开启新的线程,但是线程数不能超过7-2等于5个
    
    for (int i = 1; i < 8; i++) {
        final int taskID = i;
        threadPool.execute(new Runnable());
    }
    说明此时有7个task进入线程池
    
打印结果:
第1次任务的第1次执行
第2次任务的第1次执行
第6次任务的第1次执行
第7次任务的第1次执行
第5次任务的第1次执行
第2次任务的第2次执行
第1次任务的第2次执行
第7次任务的第2次执行
第6次任务的第2次执行
第5次任务的第2次执行
第7次任务的第3次执行
第6次任务的第3次执行
第5次任务的第3次执行
第1次任务的第3次执行
第2次任务的第3次执行
第7次任务的第4次执行
第5次任务的第4次执行
第6次任务的第4次执行
第2次任务的第4次执行
第1次任务的第4次执行
第3次任务的第1次执行
第4次任务的第1次执行
第4次任务的第2次执行
第3次任务的第2次执行
第3次任务的第3次执行
第4次任务的第3次执行
第4次任务的第4次执行
第3次任务的第4次执行
10s过后

说明同时并发了5个线程(AsyncTask#1到5),并执行第1,2,6,7,5task。task3和4被放入到queue,2个(AsyncTask#3和AsyncTask#5)是核心线程,不会终止,运行完之后运行queue中的task(task3和4)。另外3个线程(AsyncTask#1和AsyncTask#2,AsyncTask#4)也并发运行了,但是这三个线程执行完之后的10s后就终止了。。
然后queue的两个task在等待,核心线程执行完当前task,才会执行这两个task(task3和4




如果此时

public static final ExecutorService THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
此时最大的线程数4 + queue中缓存的2个task = 6 ,比上文中的7个task小,所以task7是执行不到的,同时抛出RejectedExecutionException            
打印结果:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task Test$2@55f96302 rejected from java.util.concurrent.ThreadPoolExecutor@3d4eac69[Running, pool size = 4, active threads = 4, queued tasks = 2, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
    at Test.run(Test.java:29)
    at Test.main(Test.java:59)
第1次任务的第1次执行
第2次任务的第1次执行
第6次任务的第1次执行
第5次任务的第1次执行
第1次任务的第2次执行
第2次任务的第2次执行
第5次任务的第2次执行
第6次任务的第2次执行
第1次任务的第3次执行
第2次任务的第3次执行
第5次任务的第3次执行
第6次任务的第3次执行
第1次任务的第4次执行
第2次任务的第4次执行
第6次任务的第4次执行
第5次任务的第4次执行
第4次任务的第1次执行
第3次任务的第1次执行
第4次任务的第2次执行
第3次任务的第2次执行
第3次任务的第3次执行
第4次任务的第3次执行
第3次任务的第4次执行
第4次任务的第4次执行


AsyncTask

源码

这个的原理网上一大把。。就不多分析了

onPreExecute  运行在主线程,一开始的准备工作

doInBackground  运行在子线程,所以可以做耗时的工作,运行过程中通过handler发消息给main线程,调用onProgressUpdate方法。 运行结束通过handler发消息给main线程,调用onPostExecute方法

onProgressUpdate 据上可知运行在main线程

onPostExecute 据上可知运行在main线程

给下参考博客

1.Android应用AsyncTask处理机制详解及源码分析

2.Android AsyncTask 源码解析

自己总结一下:

Android3.0以上

    @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) {
        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;
    }
    所以一个异步task只能运行一次,否则报IllegalStateException异常,默认情况使用sDefaultExecutor

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
	
	    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);
            }
        }
    }<span><span> 
</span></span>


/** 
 * An {@link Executor} that executes tasks one at a time in serial 
 * order.  This serialization is global to a particular process. 
 */  
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 
SERIAL_EXECUTOR是静态的,所以一个进程就这么一个,所有asynctask共享一个SERIAL_EXECUTOR管理

虽然THREAD_POOL_EXECUTOR这个支持最多MAXIMUM_POOL_SIZE个线程的并发,同时可以缓存128个任务,但是看SERIAL_EXECUTOR的定义可以发现,

                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
r.run();只有当前asynctask运行完全,才会去执行finally中的scheduleNext()方法,执行把下一个任务放到THREAD_POOL_EXECUTOR线程池中运行。。所以THREAD_POOL_EXECUTOR最多并发的线程数其实只有CORE_POOL_SIZE.。。。但是在运行的绝对只有1个,其它4个处于idle态,所以说是单线程执行。
所以可以得出3.0以上版本的实现asynctask是单个线程执行的,执行完一个才能执行另一个

    
    for(int i = 1 ;i <= 139 ; i++)  
    {  
        new MyAsyncTask().execute();  
    }
 
    3.0以上139个MyAsyncTask单线程执行
    
但是在android3.0以下,并没有SerialExecutor这个类,所以会支持多线程并发的方式执行,支持并发数也是我们上面所计算的128,阻塞队列可以存放10个;
也就是同时执行138个任务是没有问题的;而超过138会马上出现java.util.concurrent.RejectedExecutionException;而在3.0以上包括3.0的系统中会为单线程执行


    for(int i = 1 ;i <= 139 ; i++)  
    {  
        new MyAsyncTask().execute();  
    } 
    3.0以下,会发生异常:java.util.concurrent.RejectedExecutionException
    现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10, 此时当第11个任务提交的时候则会发生异常。
    
另一方面:
如果要在3.0以上支持asynctask的并发, 那么可以调用一下接口,但是必须自定义Executor,设置可以使用Asynctask中pulic开放的THREAD_POOL_EXECUTOR
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)


测试代码

首先定义一个异步任务,让线程休眠1s

public class MyAsynctask extends AsyncTask {
    @Override
    protected Object doInBackground(Object[] params) {
        try {
            Thread.sleep(1000);
            BaseApp.Log("doInBackground: " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

主界面

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < 150; i++) {
            new MyAsynctask().execute();
            //new MyAsynctask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
            //new MyAsynctask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }
}

情况1

new MyAsynctask().execute();

执行结果: 可以看到每1s执行一个任务。。但是第5个任务添加进来后THREAD_POOL_EXECUTOR就有五个线程了。。但是只有1个线程在运行,其它4个处于idle态,至于用那个线程执行task,结果是不固定的,,这个看cpu的调度,所有之后可能打印AsyncTask #1到5中的任何一个

11-05 17:50:46.521 28829-28855/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #1
11-05 17:50:47.524 28829-28956/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #2
11-05 17:50:48.530 28829-29033/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3
11-05 17:50:49.531 28829-29078/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 17:50:50.532 28829-29124/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5
11-05 17:50:51.533 28829-29124/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5
11-05 17:50:52.533 28829-29124/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5

............

11-05 17:51:12.636 28829-29078/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 17:51:13.637 28829-29078/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 17:51:14.644 28829-29033/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3
11-05 17:51:15.645 28829-29033/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3

............


情况2

new MyAsynctask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);

这个不用说,跟情况1完全一样,因为execute()内部调用的就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR),所以单线程执行。。


情况3

new MyAsynctask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

执行结果:force close了有没有

Caused by: java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@42c1ca70 rejected from java.util.concurrent.ThreadPoolExecutor@42b1e658[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0]


因为此时150个任务,已经超过了MAXIMUM_POOL_SIZE+128了。。。在我的机子上cpu是4核,所以最多支持9+128=137个任务的提交,否则就抛出RejectedExecutionException异常。。如果此时150改成137,那么就不要抛异常啦,大于137都会抛RejectedExecutionException异常

本质,因为此时使用的不是SERIAL_EXECUTOR。。而是THREAD_POOL_EXECUTOR,所以此时是并发的处理150个任务的。。

android3.0以下,没有SERIAL_EXECUTOR,,,所以超过一定数量的任务也会抛这个异常


情况4

for (int i = 0; i < 100; i++) {
    new MyAsynctask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

此时100小于137的,所以正常运行,可以发现,5个线程同时并发。。。

11-05 18:05:17.506 16953-16978/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #1
11-05 18:05:17.506 16953-16979/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #2
11-05 18:05:17.507 16953-16980/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3
11-05 18:05:17.507 16953-16981/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 18:05:17.508 16953-16982/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5
11-05 18:05:18.506 16953-16978/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #1
11-05 18:05:18.507 16953-16979/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #2
11-05 18:05:18.507 16953-16980/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3
11-05 18:05:18.508 16953-16981/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 18:05:18.509 16953-16982/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5
11-05 18:05:19.507 16953-16978/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #1
11-05 18:05:19.507 16953-16979/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #2
11-05 18:05:19.508 16953-16980/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #3
11-05 18:05:19.508 16953-16981/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #4
11-05 18:05:19.509 16953-16982/lbb.demo.test D/LiaBin: doInBackground: AsyncTask #5
................




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值