美文网首页Android技术
Android中线程池值得掌握一波

Android中线程池值得掌握一波

作者: 的一幕 | 来源:发表于2019-04-19 15:32 被阅读65次

    该文的起源呢,是要归结到上一篇的AsyncTask源码整理一波,也是为了更好地掌握线程池这一块的知识。下面咱们看一个原始用线程的事例:

    public class ExcuterActivity extends AppCompatActivity {
        private static final String TAG = ExcuterActivity.class.getSimpleName();
        ProgressBar progressBar;
        ProgressBar progressBar1;
        ProgressBar progressBar2;
        ProgressBar progressBar3;
    
        private Handler handler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                int what = msg.what;
                if (what == 0) {
                    progressBar1.setProgress((int) msg.obj);
                } else if (what == 1) {
                    progressBar2.setProgress((int) msg.obj);
                } else if (what == 2) {
                    progressBar3.setProgress((int) msg.obj);
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_excuter);
            progressBar1 = findViewById(R.id.progress1);
            progressBar2 = findViewById(R.id.progress2);
            progressBar3 = findViewById(R.id.progress3);
            for (int i = 0; i < 3; i++) {
                final int progress = 0;
                final int what = i;
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        excute(progress, what);
                    }
    
                }.start();
            }
    
        }
    
        private void excute(int progress, int what) {
            while (progress < 100) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    progress++;
                    Message message = handler.obtainMessage(what);
                    message.obj = progress;
                    handler.sendMessage(message);
                }
            }
        }
    }
    

    相信大家很熟悉上面的代码了吧,开启了三个线程,然后每过1秒增加一个进度,让handler处理消息。我想问如果任务很多的时候呢,是不是得创建很多个线程,所以这种做法肯定是不行的,下面就会衍生出线程池来了。其实线程池就是用来管理线程的,专门用一个队列来管理剩余的任务。android中用到的几种线程池其实主要围绕ThreadPoolExecutor来派生出来的,所以下面主要来说明该类:

    image.png
    这张图包括了ThreadPoolExecutor所有的构造器,那咱们直接去看下参数最多的构造器:
    image.png
    corePoolSize:核心线程的个数
    maximumPoolSize:线程池中最大的线程个数(最大线程个数=核心线程+非核心线程)
    keepAliveTime:非核心线程在空闲的时候等待的时间
    unit:上面参数等待的时间单位
    workQueue:线程队列,能设置该队列能承载的最多线程
    threadFactory:线程工厂,用于设置线程的名字,可以不用关心该参数
    handler:当任务超过了队列能容载的任务时,处理的策略
    先来看一个基本的例子:
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            public Thread newThread(Runnable r) {
                return new Thread(r, "thread #" + mCount.getAndIncrement());
            }
        };
    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 1,
                TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), sThreadFactory);
    

    在上面初始化了3个核心的线程,然后最大的线程是5个,非核心线程在空闲的时候等待的时间是1秒,任务队列最大允许有10个任务,定义了一个sThreadFactory只是为了打印线程的名字。

    image.png
    执行的地方换成了threadPoolExecutor来执行。
    image.png
    excute获取每个线程的进度,以及每个线程的名字。

    咋们再看下打印的日志:

    image.png
    可以看出来每过1秒钟,同时3个线程的progress加1。
    如果线程池中的线程数未达到核心线程的个数,则会立马开启一个新的核心线程去执行

    下面试着增大任务的个数为5个,其他的配置不变,看看情况会咋样:


    image.png

    可以看出来,这里用到的还是核心线程,并且将前面的1和2两个线程放到了线程队列中,等到1前面的3个任务执行完了后,让队列中的线程去处理后面的任务。

    如果线程池的个数大于核心线程的个数,并且线程队列还能装下线程,因此让核心线程排到线程队列中,等到非队列的线程任务执行完了后,才会执行队列中的线程。
    那下面把线程的个数填满线程队列,上面设置的线程队列最大容载是10个线程,10个+核心线程3个=13个。那咱们设置14个看下会发生什么,为了看到效果我把progress的界限改了下:

    image.png image.png

    再执行看下:

    image.png
    可以看得出来,核心线程总共是3个,线程队列是10个线程容量,而任务是14个,因此当线程队列满了10个的时候,还需要一个线程,因此创建了一个非核心线程4来执行任务。
    为了验证这个猜测,我们现在再增加任务到16个,看是不是创建了3个非核心线程,这里我把最多的线程个数调到7个,方便我们观察线程的动态:
    image.png
    image.png
    可以看出来确实创建了3个非核心线程,也验证了我们的结论:
    当线程队列满的时候,如果还有任务需要执行,此时需要几个线程就需要创建几个非核心线程
    上面都是未超过非核心线程的个数,那么如果线程队列也满了,而且需要剩下的线程个数超过了非线程的个数会咋样呢,这里我把任务继续调到18个,那此时需要的非核心线程是不是就是18-(3个核心线程+10个线程队列的个数)=5个非核心线程,而我们定义的非核心线程是4个,那此时看看会发生什么:
    image.png
    这里抛了一个异常信息:
    image.png

    意思是线程的总共个数是7个,不能达到需要线程的个数。因此这里可以得出结论:
    在任务需要非核心线程个数大于设置的最大非核心线程的个数时候,此时是直接抛RejectedExecutionException异常。
    说完了上面的几种情况,其实java里面给提供了几种常用的线程池,在Executors类中有如下几种线程池:
    FixedThreadPool

    image.png
    image.png image.png
    从日志也看得出来,总共是3个线程在倒腾,这个没什么好说的。
    这里可以看到最大线程数和核心线程数是相等的,说明没有非核心线程的说法了,也就是自始至终都只有核心线程。
    SingleThreadExecutor
    image.png
    image.png image.png

    看到日志大家也明白了,自始至终只有一个线程在工作。

    只有一个核心线程,和我们平常new一个thread是一个道理
    CachedThreadPool

    image.png
    该线程只有非核心线程,并且非核心线程在空闲的时候等60s就销毁了
    其他的几种线程池就自己看了,这里只是列举出一两种。

    总结

    • 如果线程池中的线程数未达到核心线程的个数,则会立马开启一个新的核心线程去执行
    • 如果线程池的个数大于核心线程的个数,并且线程队列还能装下线程,因此让核心线程排到线程队列中,等到非队列的线程任务执行完了后,才会执行队列中的线程。
    • 当线程队列满的时候,如果还有任务需要执行,此时需要几个线程就需要创建几个非核心线程。
    • 在任务需要非核心线程个数大于设置的最大非核心线程的个数时候,此时是直接抛RejectedExecutionException异常。

    相关文章

      网友评论

        本文标题:Android中线程池值得掌握一波

        本文链接:https://www.haomeiwen.com/subject/lryvwqtx.html