zoukankan      html  css  js  c++  java
  • java线程池

    1.线程池的核心概念

      线程池ThreadPoolExecutor的构造函数有多个参数,但是最重要的参数是workQueue阻塞队列这个参数。线程池正是使用了阻塞队列的特性,使得线程能够一直留在池中,而不会因为run方法执行完成而销毁。线程池的线程会从阻塞队列中取出任务执行,当阻塞队列为空时,阻塞队列会阻塞当前线程(挂起),直到有新的任务进入队列才会被唤醒。所以当目前没有任务时,线程池的线程会被挂起(不消耗cpu),线程就可以一直存在于进程中(只不过被挂起了而已)或者说存在于线程池中。这种方式体现了‘池’的概念,这些线程可以重复使用,有任务时被唤醒执行,没有任务则挂起,减少线程创建和销毁的性能消耗。
    1. corePoolSize:核心线程数,不要被这个参数的名称迷惑,其实这个参数实际上是线程池线程数。当线程池实际线程数量小于corePoolSize时,线程池将会创建新线程并加入到线程池中;大于corePoolSize时,会将任务加入队列,而不会继续创建线程,只有队列已经满的情况下,并且当前线程数小于maxPoolSize,才会创建新线程来执行任务。但是这部分线程会在keepAliveTime之后被销毁。
    2. maxPoolSize:最大线程数
    3. keepAliveTime:线程存活时间(在corePore<当前线程数<maxPoolSize情况下有用)
    4. timeUnit:存活时间的时间单位
    5. workQueue:阻塞队列(用来保存等待被执行的任务,关于workQueue参数的取值,JDK提供了4种阻塞队列类型供选择:)
      1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
      2. InkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务
      3. SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于ArrayBlockingQuene;
      4. PriorityBlockingQuene:具有优先级的无界阻塞队列;
    1. threadFactory:线程工厂,主要用来创建线程;
    2. handler:表示当拒绝处理任务时的策略,有以下四种取值
      1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
      2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
      3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
      4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 5.当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
     

    2.例子

    public class ThreadPool {
    public static void main(String[] args) {
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,10,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(5));
     
    for (int i=0;i<15;i++){
    poolExecutor.execute(new Task(i));
    System.out.println("线程池中线程数目:"+poolExecutor.getPoolSize()+",队列中等待执行的任务数目:"+
    poolExecutor.getQueue().size()+",已执行完成的任务数目:"+poolExecutor.getCompletedTaskCount());
    }
    for(;;) {
    try {
    //if (taskNum < 10) {
    Thread.currentThread().sleep(3000);
    //}
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("线程池中线程数目:" + poolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" +
    poolExecutor.getQueue().size() + ",已执行完成的任务数目:" + poolExecutor.getCompletedTaskCount());
    }
    //poolExecutor.shutdown();
    }
    }
    class Task implements Runnable {
    private int taskNum;
     
    public Task(int taskNum){
    this.taskNum = taskNum;
    }
     
     
    @Override
    public void run() {
    System.out.println("正在执行task "+taskNum);
    try {
    //if (taskNum < 10) {
    Thread.currentThread().sleep(4000);
    //}
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("task "+taskNum+"执行完毕");
    }
    }
    输出:
    正在执行task 0 线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完成的任务数目:0 正在执行task 1 线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完成的任务数目:0 线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完成的任务数目:0 正在执行task 2 线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完成的任务数目:0 正在执行task 3 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完成的任务数目:0 正在执行task 4 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完成的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完成的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完成的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完成的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:5,已执行完成的任务数目:0 线程池中线程数目:6,队列中等待执行的任务数目:5,已执行完成的任务数目:0 正在执行task 10 线程池中线程数目:7,队列中等待执行的任务数目:5,已执行完成的任务数目:0 正在执行task 11 线程池中线程数目:8,队列中等待执行的任务数目:5,已执行完成的任务数目:0 正在执行task 12 线程池中线程数目:9,队列中等待执行的任务数目:5,已执行完成的任务数目:0 正在执行task 13 线程池中线程数目:10,队列中等待执行的任务数目:5,已执行完成的任务数目:0 正在执行task 14 线程池中线程数目:10,队列中等待执行的任务数目:5,已执行完成的任务数目:0 task 1执行完毕 task 3执行完毕 task 11执行完毕 task 10执行完毕 task 4执行完毕 task 2执行完毕 task 0执行完毕 task 14执行完毕 task 12执行完毕 task 13执行完毕 正在执行task 9 正在执行task 8 正在执行task 7 正在执行task 6 正在执行task 5 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完成的任务数目:10 task 9执行完毕 task 6执行完毕 task 7执行完毕 task 8执行完毕 task 5执行完毕 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完成的任务数目:15 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完成的任务数目:15
    可以看到线程池线程的数量会维持coreSize的数量,大于core和小于max的线程会被回收。
     

    3.Executors工厂类

    Executors是ThreadPoolExecutor线程池的工厂类,根据不同的入参创建了四种不同类型的线程池,下面一一列举这四种线程池的特性。
     
    1.FixedThreadPool
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
    它是一种固定线程数量的线程池;
    在创建LinkedBlockingQueue阻塞队列时,没有指定容量。那么这就意味着该线程池永远都不会拒绝任务;
    所以keepAliveTime和handler参数就是无效的。
     
    2.SingleThreadExecutor
    public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()));
    顾名思义,它是一种只有单个线程的线程池;
    它与FixedThreadPool的区别只是在于coreThread的数量。
     
    3.CachedThreadPool
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    60L, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>(),
    threadFactory);
    它是一个可以无限扩大的线程池;
    corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
    keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
    采用SynchronousQueue阻塞队列,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程;
    因为SynchronousQueue阻塞队列的特性,它比较适合处理执行时间比较小的任务。
     
    4.ScheduledThreadPool
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
    它用来处理延时任务或定时任务。
     
    CachedThreadPool比较难以理解因此这里提供一个例子
    package threadpool;

    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;

    /**
    *
    * @author : LQ
    * @since : 2019/1/18 14:50 Description:
    */
    public class ThreadPool {
    public static void main(String[] args) {
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(0,1000,60, TimeUnit.SECONDS,new SynchronousQueue());

    for (int i=0;i<100;i++){
    poolExecutor.execute(new Task(i));
    }
    for(;;) {
    System.out.println("线程池中线程数目:" + poolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" +
    poolExecutor.getQueue().size() + ",已执行完成的任务数目:" + poolExecutor.getCompletedTaskCount());
    }
    //poolExecutor.shutdown();
    }
    }
    class Task implements Runnable {
    private int taskNum;

    public Task(int taskNum){
    this.taskNum = taskNum;
    }


    @Override
    public void run() {
    System.out.println("thread name:"+Thread.currentThread().getName());
    }
    }
    输出:
    线程池中线程数目:14,队列中等待执行的任务数目:0,已执行完成的任务数目:100
     
    可以看到虽然提交了100个任务,但其实创建的线程只有7个(每次创建的线程数量都不一样)。如果任务的处理时间很长,那么就很可能会创建100个线程了。
  • 相关阅读:
    内网渗透之文件下载
    从跨域与同源策略谈CSRF防御与绕过
    内网渗透之权限维持
    一次关于shiro反序列化漏洞的思考
    冰蝎&哥斯拉 流量特征分析
    第三方提权之数据库提权
    APP渗透测试之安卓APP抓包
    从零开始的信息搜集(二)
    从零开始的信息搜集(一)
    python 进程
  • 原文地址:https://www.cnblogs.com/lanqi/p/11585159.html
Copyright © 2011-2022 走看看