一起Talk Android吧(第三百七十一回:多线程之线程池扩展)

Posted talk_8

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起Talk Android吧(第三百七十一回:多线程之线程池扩展)相关的知识,希望对你有一定的参考价值。

文章目录

各位看官们,大家好,上一回中咱们说的是android中多线程之线程池回顾的例子,这一回中咱们介绍的例子是多线程之线程池扩展。闲话休提,言归正转。让我们一起Talk Android吧!

使用步骤

看官们,我们在上一章回中回顾了线程池相关的内容,并且重点介绍了ThreadPoolExecutor类的构造方法,本章回中将通过具体的示例代码来介绍如何使用此方法来创建线程池。不过线程池的使用步骤不变,还是三步曲:

  • 1.创建线程池
  • 2.创建线程并且加入到线程池中(execute()/submit());
  • 3.关闭线程池(程序异常时关闭)

示例程序

下面是示例程序,请大家参考

public class ThreadPool 
    //ExecutorService接口代表线程池,通过使用它的实现类ThreadPoolExecutor,创建此类的对象相当于创建线程池
    public static void main(String[] args) throws Exception
        int mCorePoolSize = 2;
        int mMaxPoolSize = 3;
        int mPoolKeepAliveTime = 2;

        ExecutorService executorService = new ThreadPoolExecutor(mCorePoolSize,mMaxPoolSize,mPoolKeepAliveTime, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        Thread thread1 = new Thread()
            @Override
            public void run() 
                System.out.println(Thread.currentThread().getName()+" is running...");
                try 
                    TimeUnit.SECONDS.sleep(5);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            
        ;

        Thread thread2 = new Thread(()-> System.out.println(Thread.currentThread().getName()+" is running...");
            try 
                TimeUnit.SECONDS.sleep(3);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        );
        Thread thread3 = new Thread(()-> System.out.println(Thread.currentThread().getName()+" is running..."););
        Thread thread4 = new Thread(()-> System.out.println(Thread.currentThread().getName()+" is running..."););
        Thread thread5 = new Thread(()-> System.out.println(Thread.currentThread().getName()+" is running..."););

        /*
        executorService.execute(thread1);
        executorService.execute(thread2);
        executorService.execute(thread3);
        executorService.execute(thread4);
        executorService.execute(thread5);

         */
        /* ************* step 1 ************** */
        //使用两个core线程处理任务
        executorService.submit(thread1);
        executorService.submit(thread2);

        /* ************* step 2 ************** */
        //使用长度为2的任务队列存放任务,core线程处理完任务后从任务队列中拿出任务继续处理
        executorService.submit(thread1);
        executorService.submit(thread2);

        /* ************* step 3 ************** */
        //core线程正在处理任务,而且任务队列也满了会创建一个临时线程来处理任务
        executorService.submit(thread3);

        /* ************* step 4 ************** */
        //core线程正在处理任务,而且任务队列也满了会创建一个临时线程来处理任务,
        //创建了临时任务也在忙,再创建一个临时任务,但是超过线程池最大线程数3,引发异常
        executorService.submit(thread4);

        executorService.shutdown();
    

程序演示

我们在上面的代码中创建了一个自定义的线程池,其中包含2个核心线程、3个最大线程,任务队列的长度为2.大家先把step1-4这部分代码注释掉,然后依次释放step1-4注释中的程序,可以得到以下运行结果

演示1

//第一步:线程中只有两个线程在运行
pool-1-thread-2 is running...
pool-1-thread-1 is running...

演示2

//第二步:线程中只有两个线程在运行,任务队列中有两个任务,完成当作任务后再从任务队列中获取新任务
pool-1-thread-2 is running...
pool-1-thread-1 is running...
pool-1-thread-2 is running...
pool-1-thread-1 is running...

演示3

//第三步:线程中有两个线程在运行,任务队列中有两个任务,再来一个任务时创建了临时线程(Thread-3)来完成任务
pool-1-thread-2 is running...
pool-1-thread-3 is running...
pool-1-thread-1 is running...
pool-1-thread-3 is running...
pool-1-thread-2 is running...

演示4

//第四步线程中有两个线程在运行,任务队列中有两个任务,再来一个任务时创建了临时线程(Thread-3)来完成任务
//再来另外一个任务时需要创建临时线程(Thread-4)来完成任务,但是超过了线程池中最大线程数量(3),因此发生异常
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1b2c6ec2[Not completed, task = java.util.concurrent.Executors$RunnableAdapter@30dae81[Wrapped task = Thread[Thread-3,5,main]]] rejected from java.util.concurrent.ThreadPoolExecutor@4edde6e5[Running, pool size = 3, active threads = 3, queued tasks = 2, completed tasks = 0]
  at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2057)
  at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:827)
  at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1357)
  at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:118)
  at com.thread.ex.ThreadPool.main(ThreadPool.java:75)
pool-1-thread-2 is running...
pool-1-thread-1 is running...
pool-1-thread-3 is running...
pool-1-thread-3 is running...
pool-1-thread-2 is running...

这个示例中包含了多个操作,希望大家自己动手去实践一下,这样才能体会到自定义线程池背后的运行逻辑。

内容总结

当然了,如果项目中的线程数量不是特别多也可以使用静态方法来创建线程池,毕竟这种方法使用方便一些。如果项目中线程数量特别多,最好还是以自定义的方式创建线程池。比如阿里的编码规范中就明确要求不能使用静态方法来创建线程池。

最后我们对线程池的知识做统一的总结:

  • 1.线程池的使用方法掌握三步曲就可以;
  • 2.创建线程池时依据项目中线程的数量来决定使用静态方法还是自定义方法创建线程池;
  • 3.添加线程到线程池中时使用submit()方法多一些,毕竟它可以添加多种线程到线程池中;
  • 4.线程池通常不需要关闭,如果遇到异常了最好关闭线程池;

看官们,关于Android中多线程之线程池扩展的例子咱们就介绍到这里,欲知后面还有什么例子,且听下回分解!

以上是关于一起Talk Android吧(第三百七十一回:多线程之线程池扩展)的主要内容,如果未能解决你的问题,请参考以下文章

一起Talk Android吧(第三百七十四回:多线程之大结局)

一起Talk Android吧(第三百七十回:多线程之线程池回顾)

一起Talk Android吧(第三百七十二回:Timer的陷阱)

一起Talk Android吧(第三百七十五回:如何使用ViewPager2)

一起Talk Android吧(第三百七十六回:如何使用TabLayout)

一起Talk Android吧(第三百七十八回:给ViewPager添加indicator)