关于ThreadPoolExecutor要注意的问题

117 阅读4分钟
原文链接: mp.weixin.qq.com

小A打算跟大家分享内推赚取的佣金,只要通过小A内推成功,小A就会把内推的佣金跟推荐者平分喔。具体规则可以看文末 内推规则。悄悄告诉你,内推奖励金不低。

之前我们说过关于线程池的问题,我们可以用Executors的各种方法来获取不同的ThreadPoolExecutor来满足需求。但是当我们需要自定义线程池的时候需要注意些什么呢?

ThreadPoolExecutor的参数含义

ThreadPoolExecutor的构造方法有几个用的多的参数,它们的含义分别是· corePoolSize:线程池的基本大小· maximumPoolSize:当任务队列满时允许扩展到的线程池的线程数量· workQueue:存放任务队列的BlockingQueue· handler:当任务队列满时的处理策略

之前说过可以给AsyncTask指定线程池,我们用不同的参数构造线程池来验证效果。

LinkedBlockingQueue

public class DemoAsyncTask extends AsyncTask {    private static final String TAG = "DemoAsyncTask";    private static BlockingQueue<Runnable> blockingQueue = new LinkedBlockingDeque<>(3);    private static Executor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0,            TimeUnit.MICROSECONDS, blockingQueue);    @Override    protected Object doInBackground(Object[] objects) {        try {            Thread.sleep(1000);        } catch (Exception e) {            e.printStackTrace();        }        Log.d(TAG, "doInBackground: num: " + objects[0]);        return null;    }    public void demoExecute(String... params) {        this.executeOnExecutor(threadPoolExecutor, params);    }}

指定一个LinkedBlockingQueue给线程池,然后我们从外部调用,

for(int i = 0; i < 50; i ++) {  new DemoAsyncTask().demoExecute(String.valueOf(i));}

这段代码运行就会崩溃,原因是

我们给线程池分配了50个任务,但是任务队列最大只能存放3个任务,当队列满时,系统会抛出RejectedExecutionException异常

解决这个问题有两种方法,一种是不给LinkedBlockingQueue指定队列大小。因为默认情况下 LinkedBlockingDeque会让队列不断增长以存放新进来的数据。这种方式能让50个任务正常进行。

另外一种是用 handler 参数指定队列满时的处理策略,代码可以改成下面这样

private static Executor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0,        TimeUnit.MICROSECONDS, blockingQueue, new ThreadPoolExecutor.DiscardOldestPolicy());

这是ThreadPoolExecutor的其中一个默认策略,我们直接看输出就明白了,

04-19 12:42:35.991 6167-6199/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 204-19 12:42:35.991 6167-6198/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 104-19 12:42:35.991 6167-6196/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 004-19 12:42:36.991 6167-6199/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 4704-19 12:42:36.991 6167-6198/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 4804-19 12:42:36.991 6167-6196/com.phoenix.mkdirdemo D/DemoAsyncTask: doInBackground: num: 49

所以就是说,这个策略会把队列里老的请求丢弃,保留最新请求。因为一开始的任务0-2正在执行中,而队列已满,因此最终只能保留47-49最后三个请求。这个策略适合用在fast fail场景,快速的反馈给用户失败而不是让用户等待。

ArrayBlockingQueue

用 ArrayBlockingQueue会有个问题,因为它的存储方式是数组,需要一开始就指定大小,所以必须得指定 handler来做队列满时的处理策略。如果不指定handler的话只要任务数量超过 ArrayBlockingQueue 指定的大小,就会抛出 RejectedExecutionException异常。

回到 AsyncTask

其实 AynscTask 默认会有个线程池,这个线程池的大小是128,可以从源码看出来

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

所以如果用默认的AsyncTask来处理大量的任务的话是有可能导致应用崩溃的。

内推规则

· 只要把公众号发给想要找工作的朋友让朋友关注,然后把朋友的微信ID回复给小A,小A就会在成功推荐后联系你,然后在成功入职后跟你平分佣金。· 想找工作的朋友关注后点击面试 -> 内推,就可以免费获取内推机会。· 每推荐成功一个都会有奖励金哦!