添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1、为什么JDK1.5引入线程池
在Java中,如果每个任务都创建一个新的thread,开销是非常大的。除了创建和销毁线程的时间开销外,还消耗大量的系统资源。为了规避以上问题,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有的线程对象来进行服务,这就是线程池引入的原因。

2、ThreadPoolExecutor类分析
(a)ThreadPoolExecutor构造函数:

public ThreadPoolExecutor(int corePoolSize,
  int maximumPoolSize,
  long keepAliveTime,
  TimeUnit unit,
  BlockingQueue<Runnable> workQueue,
  ThreadFactory threadFactory,
  RejectedExecutionHandler handler);

(1)corePoolSize 核心线程数
(2)maximumPoolSize 最大线程数
(3)keepAliveTime 线程被回收前的空闲时间
(4)workQueue 任务队列
(5)threadFactory 线程创建工厂
(6)handler 线程池对拒绝任务的处理策略

(b)ThreadPoolExecutor运行原理:
ThreadPoolExecutor将根据corePoolSize和maximumPoolSize设置的值调整线程池大小。当新任务调用方法execute(Runnable)提交时,如果运行的线程少于corePoolSize,则创建新线程来处理请求。如果正在运行的线程数等于corePoolSize时,则新任务被添加到队列中,直到队列满。当队列满了后,会继续开辟新线程来处理任务,但不超过最大线程数。当任务队列满了并且已开辟了最大线程数,此时又来了新任务,ThreadPoolExecutor会拒绝服务。
(c)keepAliveTime注意事项: * Timeout in nanoseconds for idle threads waiting for work. * Threads use this timeout when there are more than corePoolSize * present or if allowCoreThreadTimeOut. Otherwise they wait * forever for new work. private volatile long keepAliveTime;说明:当线程空闲超过keepAliveTime,非核心线程会被回收,若allowCoreThreadTimeOut为true则核心线程也会被回收。 * If false (default), core threads stay alive even when idle. * If true, core threads use keepAliveTime to time out waiting * for work. private volatile boolean allowCoreThreadTimeOut;

说明:默认情况下,核心线程不会被回收;当allowCoreThreadTimeOut为true,核心线程也会被回收。

3、任务队列

BlockingQueue<Runnable> workQueue;
workQueue是一个BlockingQueue接口的对象,仅用于存放Runnable对象。
(1)直接提交(如SynchronousQueue)
直接提交策略表示线程池不对任务进行缓存。新进任务直接提交给线程池,当线程池中没有空闲线程时,创建一个新的线程处理此任务。这种策略需要线程池具有无限增长的可能性。
Executors.newCachedThreadPool()使用SynchronousQueue创建线程池。
(2)无界队列(如不具有预定义容量的LinkedBlockingQueue)
LinkedBlockingQueue将导致当所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。
Executors.newFixedThreadPool(3)使用LinkedBlockingQueue创建线程池。
Executors.newSingleThreadExecutor()使用LinkedBlockingQueue创建线程池。
(3)有界队列(如ArrayBlockingQueue)
有界队列(如ArrayBlockingQueue)有助于防止资源耗尽当最大线程数有限时,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷。
(4)优先级队列(如PriorityBlockingQueue)
(5)DelayedWorkQueue
DelayedWorkQueue是ScheduledThreadPoolExecutor的静态内部类。
Executors.newScheduledThreadPool(3)使用DelayedWorkQueue创建线程池。

4、拒绝策略
接口RejectedExecutionHandler提供了拒绝任务处理的自定义方法的机会。在ThreadPoolExecutor中已经包含四种拒绝策略。
(1)AbortPolicy
拒绝策略:抛出运行时异常RejectedExecutionException。
这种策略丢弃任务,并抛出异常。(jdk默认策略)
(2)DiscardPolicy
拒绝策略:不能执行的任务将被丢弃。
这种策略什么都没做。
(3)DiscardOldestPolicy
拒绝策略:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
}
该策略稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。
(4)CallerRunsPolicy

拒绝策略:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
}
这个策略不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。 1、为什么JDK1.5引入线程池在Java中,如果每个任务都创建一个新的thread,开销是非常大的。除了创建和销毁线程的时间开销外,还消耗大量的系统资源。为了规避以上问题,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有的线程对象来进行服务,这就是线程池引入的原因。 2、ThreadPoolExecutor类分析(a)ThreadPoolExecutor构... 模拟实现银行业务调度系统逻辑,具体需求如下: • 银行内有6个业务窗口,1 - 4号窗口为普通窗口,5号窗口为快速窗口,6号窗口为VIP窗口。 • 有三种对应类型的客户:VIP客户,普通客户,快速客户(办理如交水电费、电话费之类业务的客户)。 • 异步随机生成各种类型的客户,生成各类型用户的概率比例为: VIP客户:普通客户:快速客户 = 1 :6 :3。
我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销...
本文用示例来介绍 Java 中的队列的用法。包括:DelayQueue,ConcurrentLinkedQueue,BlockingQueue。 文章最后有一个常见的面试题代码实例:机器要对手机按顺序做如下任务:生产、打包、发货。消费者等待收货,如何使用BlockingQueue完成? DelayQueue DelayQueue:只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue就是基于Priority... public class CyclickBarrierTest { public static void main(String[] args) { int childSize = 10; int mainSize = 20; int ci... 重用 线程池 中的线程, 避免因为线程的创建和销毁锁带来的性能开销。 有效控制 线程池 的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。 能够对线程进行简单的管理, 并提供定时执行以及指定间隔循环等执行功能。 2. ThreadPoolExecutor ThreadPoolExecutor是 线程池 的真正实现,它的构造方法提供了一系列参数来配置 线程池 , 下面介绍ThreadPoolExecutor的构造方法中的各个餐厨的含义, 这些参数将会直接影响 线程池 的功能特性。
方法一:在子线程中通过join()方法指定顺序 通过join()方法使当前线程“阻塞”,等待指定线程执行完毕后继续执行,比如在线程thread2中,加上一句thread1.join(),其意义在于,当前线程2运行到此行代码时会进入阻塞状态,直到线程thread1执行完毕后,线程thread2才会继续运行,这就保证了线程thread1与线程thread2的运行顺序 方法二:在主线程中通过join()方法指定顺序 上面我们是在子线程中指定join()方法,我们还可以在主线程中通过join()方法让主线程阻塞等待
大概说下问题(类似),就是新建一个 线程池 ,核心线程是3个,最大线程数是6个,阻塞队列是12,过期时间是20s,假设每个线程处理完一个任务需要1s。如果一次性来了18 个任务,也就是线程全开,队列塞满,那么:3s之后(也就是任务搞完了),每2s来一个任务,20s之后非核心线程会关闭么? 乍一看到这个问题,我也是懵了,非核心线程不是到点了就关掉了么?哦,你任务处理完了,我还有6个线程,每2s来一个,处理一个要1s,就这事?我一个线程就能搞定啊,还要啥6个线程?非核心线程可以关了啊!还在那干啥子?浪费性能!
import java .util.ArrayList; import java .util.EmptyStackException; import java .util.LinkedList; import java .util.List;...
Java 中使用固定 线程池 时,可以使用 ` java .util.concurrent.ThreadPoolExecutor` 类来创建 线程池 。您可以通过在创建 线程池 时指定 拒绝策略 的方式来配置 拒绝策略 。 例如,下面的代码创建了一个大小为 5 的固定 线程池 ,并指定了一个 拒绝策略 : import java .util.concurrent.ArrayBlockingQueue; import java .util.concurrent.ThreadPoolExecutor; import java .util.concurrent.TimeUnit; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个大小为5的固定 线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10), new ThreadPoolExecutor.CallerRunsPolicy()); // 将任务提交到 线程池 executor.execute(() -> { // 任务代码 在这个例子中, 拒绝策略 是 `ThreadPoolExecutor.CallerRunsPolicy`,这意味着如果 线程池 已满且无法再接受新任务,则调用线程将会执行新任务。 除了 `CallerRunsPolicy` 之外,还有其他几种常见的 拒绝策略 : - `ThreadPoolExecutor.AbortPolicy`:直接抛出异常。 - `ThreadPoolExecutor.DiscardPolicy`:直接丢弃新任务。 - `ThreadPoolExecutor.DiscardOldestPolicy`:丢弃队列中最老的任务,然后尝试再次提交新任务。 您可以根据自己的需要选择合适的拒