线程池的学习

线程池:由于在平常的线程使用中,会频繁的创建和销毁线程,因为我们之前所创建的是单个的线程,使用完毕后就自动销毁,这样会大大的降低系统的效率,因为创建和销毁线程都需要时间。

所以急需一种方法,在线程执行完毕任务后,并不会立即的销毁,而是还可以继续执行下一个任务。Java中线程池就实现了这种想法。

1.首先我们来了解一下这几个线程池中抽象类,接口之间的关系

  1. Executor(接口):是一个顶层接口; 里面只有一个方法:

               void execute(Runnable runnable):用来执行传递进来的任务的,没有返回值。

  1. ExecutorService(接口):继承了Executor接口,并声明了一些方法:

              Future<?>  submit ( Runnable / Callable):用来执行任务,并带有返回值;

              Void shutDown():关闭线程池;以及invoke方法;

  1. AbstractExecutorService(抽象类):实现了Executor,ExecutorService接口,方法主要是ExecutorService接口里的方法;

             Future<?>  submit ( Runnable / Callable):用来执行任务,并带有返回值;

             void shutDown():关闭线程池;以及invoke方法;

  1. ThreadPoolExecutor(类);继承AbstractExecutorService抽象类,也实现了Executor,ExecutorService接口(传递性);几个重要的方法:

             void    execute(Runnable):执行任务,其实是Executor中声明的方法,通过该方

             法可以向线程池中提交一个任务,交由线程池去执行。;

  

             Future <?> submit(Runnable / Callable<>):执行任务,并带有返回值,其实是ExecutorService中声明的方法,也是向线程池提交任务                 的,实际实现时还是调用了execute()方法,只不过利用Future来获取任务的执行结果。

 

             Shutdown():用来关闭线程池。

 

           (ThreadPoolExecutor构造方法中参数的意义):

              corePoolSize : 核心池的大小。在创建线程池后,默认情况下线程池中没有一个线程,而是等待任务来后,才会创建线程去执行任务。除                  非调用了prestartAllCoreThread()和平pestartCoreThread()方法,即预创建线程(创建所有corepollsize个线程或者一个线程)。当线                程数达到了corepoolsize再有任务过来时,就会把任务放入缓存队列中。

 

             maximumPoolSize:线程池中的最大线程数。

 

             KeepAliveTime:表示线程在没有任务时最多保持多久会结束默认情况下只有当线程池中的线程数大于corepoolsize时才会起作用。

 

             Unit:表示KeepAliveTime的时间单位,有7种取值。

 

             workQueue:一个阻塞队列,用来存储等待执行的任务(任务缓存队列)。一般的阻塞队列会有以下几种选择:ArrayBlockingQueue,                     LinkedBlockingQueue,SynchronousQueue三种形式一般采用 LinkedBlockingQueue 和 SynchronousQueue。

 

            ThreadFactory: 线程工厂,主要用来创建线程。

 

             Handler:表示拒绝处理任务的策略。

 

2.线程池的几个状态:

由一个 volatile变量和4个 static final int 变量表示。

  1. Volatile int runState  :表示线程的状态,volatile关键字保证了线程之间对runState的可见性。(Volatile变量取值为下面几个static变量)
  2. Static final int Running (执行); static final int shutdown(线程池关闭);static final int stop(阻塞);terminated(终结)
  3. 执行状态的切换: 
  4. Running(执行状态):当创建好线程池,初始时;
  5. Shutdown(状态):当调用了shutdown()方法,线程池会处于该状态。此时线程池将不会接受新任务,而是等待当前所有任务执行完毕;
  6. Stop(状态):当调用了shutdownnow()后,线程池将不会接受新的任务,并去尝试终止正在执行的任务;

      (总结)当处于Shutdown和Stop状态的线程池,当所有的工作线程已经销毁,任务缓存队列已经清空,或者已经执行结束,线程池将被设置                           为:Terminated(终结)状态。

3.线程池中任务的执行:

    3.1,再来回顾下ThreadPoolExecutor(类)中构造方法的几个重要参数

       Corepoolsize:线程池的大小,拥有的线程的数量(核心线程池);

 

       Maximumcorepoolsize:也是线程池的大小,不过是当任务突然增大时的一种补救措施(扩大当前线程数量);

 

       Largestpoolsize:记录量,用来记录线程池中曾经有的最大的数字。

 

       Poolsize:线程池中的当前的线程数

   3.2ThreadPoolExecutor(类)中执行任务的execute()方法,因为submit方法底层也调用了execute(),所以只分析execute方法;

 

    当任务提交给线程池之后的处理策略

    如果当前线程池的大小(poolsize)小于corepoolsize(核心线程池大小),则每来一个任务就会创建一个线程去执行;

 

    如果当前线程池的大小(poolsize)>=corepoolsize(核心线程池大小),则每来一个任务就会尝试将其添加到任务缓存队列中,若添加成功,该      任务就会等待有空闲的线程来执行它;若添加失败(一般是任务缓存队列已满),就会尝试创建一个线程去执行这个任务;

 

    如果当前线程池大小(poolsize)达到maximumpoolsize,就会采取任务拒绝策略进行处理。

 

   (注意)如果当前线程池大于maximumpoolsize,则在某一线程,空闲时间超过KeepAliveTime后,线程将会被终止,直到线程池的数量不大于            corepoolsize;如果设置了核心池中线程的存活时间,那么线程池中的线程空闲时间超过KeepAliveTime,线程也会被终止。

4.线程池中线程的初始化:

4.1首先默认情况下,创建完线程池后,线程池中是没有线程的,当有任务来时才会创建线程。

若要求在创建完线程池后就有线程,可调用如下方法:

prestartCoreThread():初始化一个核心线程;

prestartAllCoreThreads():初始化所有的核心线程;à等待任务队列中有任务的到来。

 

5.任务缓存队列及排队策略:

   任务缓存队列(work queue):用来存放等待执行的任务。数据类型为:BlockingQueue<Runnable>,通常可以取下面三种类型:

1) ArrayBlockingQueue:基于数组的先进先出队列;

2) LinkedBlockingQueue:基于链表的先进先出队列;

3) SynchronousQueue:该队列不会保存提交的任务,而是直接新建一个线程来执行新来的任务。

6.任务拒绝策略:

   如果当前的线程数已经达到了maximumpoolsize,并任务缓存队列已满,再来任务时就会采取任务拒绝策略。(通常有以下4种策略):

  1):ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

  2):ThreadPoolExecutor.DiscarPolicy:丢弃任务,但不抛出异常;

  3):ThreadPoolExecutor.DiscarOldestPolicy:丢弃任务队列中最前面的任务,执行最新的任务;

  4):ThreadPoolExecutor.CallerRunPolicy:由调用线程处理该任务。

 

7.线程池的关闭:

ThreadPoolExecutor类中有两种方法用于线程池的关闭:

Shutdown():调用该方法后,线程池将不再接受新的任务,等待线程池任务缓存队列执

完毕后,关闭线程池。

Shutdownnow():调用该方法后,立即关闭线程池,并尝试中断正在执行的任务,清空任务队列中的任务。,返回尚未执行的任务。

 

8.线程池容量的动态调整:

ThreadPoolExecutor类中提供了动态调整线程池大小的方法:

Setcorepoolsize():设置核心池的大小;

Setmaximumpoolsize():设置线程池的最大容量大小。

 

此处有一个使用ThreadPoolExecutor的实例(创建线程池,去执行任务)

 1 public class ThreadPoolExecutorTest {
 2     public static void main(String[] args) {
 3         //定义线程池
 4         ThreadPoolExecutor threadPoolExecutor =
 5                 new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
 6                         new LinkedBlockingQueue<>(5));
 7 
 8         for (int i = 0; i < 15; i++) {
 9             Mytask mytask = new Mytask(i);
10             threadPoolExecutor.execute(mytask);
11 
12             System.out.println("当前线程池中的线程数:"+threadPoolExecutor.getPoolSize()+
13                     "队列中等待执行的线程数目:"+threadPoolExecutor.getQueue().size()+
14                     "已经执行完的线程的数目:"+threadPoolExecutor.getCompletedTaskCount());
15         }
16         threadPoolExecutor.shutdown();  //关闭线程池
17     }
18 }
19 
20 class Mytask implements Runnable{
21 
22     int num;
23 
24     public Mytask(int num) {
25         this.num = num;
26     }
27 
28     @Override
29     public void run() {
30         System.out.println("task:"+num+"正在执行任务!");
31         try {
32             Thread.sleep(100);
33             System.out.println("task:"+num+"执行完毕!");
34         } catch (InterruptedException e) {
35             e.printStackTrace();
36         }
37     }
38 }

 

 

9.在Java doc中使用Executors类中提供的几个静态方法,来创建线程池:

 Executors类直接只继承自Object。其中的几个静态方法来创建线程池:

 1):Executors . newCachedThreadPool(); 创建一个缓冲池,缓冲池容量大小为:Integer .Max_VALUE;

 2):Executors .newSingleThreadExecutor(): 创建容量为一的缓冲池;

 3):Executors . newFixedThreadPool(int): 创建固定容量大小的缓冲池。

从这三个静态方法的源码来看,底层还是调用了ThreadPoolExecutor的构造方法,只不过参数已经配好了。

其中:

newFixedThreadPool创建的线程池corepoolsize和maximumpoolsize值是相等的,它使用的是LinkedBlockingQueue;

 

newSingleThreadExecutor将corepoolsize和maximumpoolsize的值都设为1,也是用LinkedBlockingQueue;

 

newCachedThreadPool将corepoolsize设为0,将maximumpoolsize设置为Integer.MAX_VALUE,使用的是SynchronousQueue,也就是来了任务就创建任务运行,当线程空闲60秒,就销毁线程

另见:Java Util Concurrent包中一些接口和类的使用:

 https://www.cnblogs.com/xbfchder/p/10975146.html 
posted @ 2019-06-20 17:43  德鲁大叔817  阅读(230)  评论(0编辑  收藏  举报