关于java:线程池

51次阅读

共计 4358 个字符,预计需要花费 11 分钟才能阅读完成。

1. 线程池是什么

线程池(Thread Pool)是一种基于池化思维治理线程的工具,经常出现在多线程服务器中,如 MySQL。

咱们都晓得线程的创立和销毁都须要肯定的资源开销,升高了计算机的整体性能。那么有没有一种方法能防止频繁的线程创立和销毁呢?基于此就引出了线程池的概念,应用线程池能够带来一系列益处:

  • 升高资源耗费:通过池化技术反复利用已创立的线程,升高线程创立和销毁造成的损耗。
  • 进步响应速度:工作达到时,无需期待线程创立即可立刻执行。
  • 进步线程的可管理性:线程是稀缺资源,如果无限度创立,不仅会耗费系统资源,还会因为线程的不合理散布导致资源调度失衡,升高零碎的稳定性。应用线程池能够进行对立的调配、调优和监控。
  • 提供更多更弱小的性能:线程池具备可拓展性,容许开发人员向其中减少更多的性能。比方延时定时线程池 ScheduledThreadPoolExecutor,就容许工作延期执行或定期执行。

2. 线程池的设计

Java 中 JDK8 的线程池外围实现类是 ThreadPoolExecutor,咱们首先来看一下 ThreadPoolExecutor 的 UML 类图,理解下 ThreadPoolExecutor 的继承关系。

ThreadPoolExecutor 实现的顶层接口是 Executor,顶层接口 Executor 提供了一种思维:将工作提交和工作执行进行解耦。用户无需关注如何创立线程,如何调度线程来执行工作,用户只需提供 Runnable 对象,将工作的运行逻辑提交到执行器 (Executor) 中,由 Executor 框架实现线程的调配和工作的执行局部。ExecutorService 接口减少了一些能力:(1)裁减执行工作的能力,补充能够为一个或一批异步工作生成 Future 的办法;(2)提供了管控线程池的办法,比方进行线程池的运行。AbstractExecutorService 则是下层的抽象类,将执行工作的流程串联了起来,保障上层的实现只需关注一个执行工作的办法即可。最上层的实现类 ThreadPoolExecutor 实现最简单的运行局部,ThreadPoolExecutor 将会一方面保护本身的生命周期,另一方面同时治理线程和工作,使两者良好的联合从而执行并行任务。

3. 线程池的实现

3.1. 通过 Executors 提供四种线程池:

1、newCachedThreadPool 创立一个可缓存线程池,如果线程池长度超过解决须要,可灵便回收线程,若无可回收,则新建线程;线程池无限大,当执行第二个工作时第一个工作已实现,会复用执行第一个工作的线程,而不是新建线程。

2、newFixedThreadPool 创立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中期待;初始线程数和最大线程数一样,如果要执行的线程大于初始线程数,则会将多余的线程工作退出到缓存队列中期待执行。

3、newScheduledThreadPool 创立一个定长线程池,反对定时及周期性工作的执行;

4、newSingleThreadExecutor 创立一个单线程化的线程池,它只会用惟一的工作线程来执行工作,保障所有工作依照指定程序 (FIFO,LIFO, 优先级) 执行;

通过 Executors 创立的线程池有个致命毛病,以 newCachedThreadPool 来说
public class ThreadPoolDemo {public static void main(String[] args)throws Exception {ExecutorService threadPool = Executors.newCachedThreadPool();
    }
}

咱们点进 newCachedThreadPool()办法会看到如下内容:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
// 咱们发现第二个参数为 Integer.MAX_VALUE,也就是最大的工作线程数为 Integer.MAX_VALUE,如果超高并发过去会间接 OOM。

所以咱们得通过 new ThreadPoolExecutor()来本人指定参数。

3.2 通过 ThreadPoolExecutor 来创立线程池

构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize:示意外围线程的数量,就是线程池中最好要保障的线程数量。
  • maximumPoolSize:最大线程数量,就是当工作很多时,能工作的最大线程数。
  • keepAliveTime:线程的闲暇工夫。就是当理论工作的线程数小于最大线程数的时候,就有局部线程是处于闲暇的状态,当这些闲暇线程的闲暇工夫达到 keepAliveTime 就会被干掉。然而要保障起码线程数为 corePoolSize。
  • unit:闲暇工夫都单位。
  • workQueue:工作队列,就是当工作线程数达到 corePoolSize 之后的工作会放入到工作队列中。
  • threadFactory:线程工厂,用于创立线程。
  • handler:回绝处理器,就是当工作线程达到最大工作线程并且工作队列曾经满了状况下,线程池应该怎么做。

3.3 线程池的工作流程。

流程图如下:

当工作过去时,首先会先去判断线程池中工作的线程数量是否达到外围线程数,如果没达到就间接执行,如果达到了就查看工作队列是否满。如果没满就将工作放入到工作队列中,如果满了就减少工作线程数来解决工作。如果工作线程和队列都满了的话就会用制订的策略去回绝工作。

3.4 回绝策略

  • AbortPolicy(默认):间接抛出异样 RejectedExecutionException 异样阻止零碎失常运行。
  • CallerRunsPolicy:调用者运行是一种调节机制,该策略既不会摈弃工作,也不会摈弃异样,而是将某些工作回退到调用者,从而升高新工作的流量。
  • DiscardOldestPolicy:抛弃队列中期待最久的工作,而后把当前任务退出队列中尝试再次提交当前任务。
  • DiscardPolicy:间接抛弃工作,不予解决也不抛出异样。如果容许工作失落,这是最好的一种计划。

代码演示回绝策略:

1.AbortPolicy

public static void main(String[] args)throws Exception {
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
    try {for (int i = 0; i < 10; i++) {poolExecutor.execute(()->{System.out.println(Thread.currentThread().getName()+"\t 办理业务");
            });
        }
    } catch (Exception e) {e.printStackTrace();
    } finally {poolExecutor.shutdown();
    }
    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
}

也就是说明当工作线程和工作队列都满了之后线程池会回绝工作间接报错。

2.CallerRunsPolicy

public static void main(String[] args)throws Exception {
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());
    try {for (int i = 0; i < 10; i++) {poolExecutor.execute(()->{System.out.println(Thread.currentThread().getName()+"\t 办理业务");
            });
        }
    } catch (Exception e) {e.printStackTrace();
    } finally {poolExecutor.shutdown();
    }
    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
}

也就是说明当工作线程和工作队列都满了之后会将工作返还给调用线程池的人,让他去解决。

3.DiscardOldestPolicy 和 DiscardPolicy

public static void main(String[] args)throws Exception {
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,5,1L,TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy());
    try {for (int i = 0; i < 10; i++) {poolExecutor.execute(()->{System.out.println(Thread.currentThread().getName()+"\t 办理业务");
            });
        }
    } catch (Exception e) {e.printStackTrace();
    } finally {poolExecutor.shutdown();
    }
    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
}

一共输入了 10 条记录,阐明有一条音讯被抛弃了。

正文完
 0