关于线程池的一些概念

目录

绪论

一.Java线程池之间的继承关系

二.线程池的状态

三.线程池的构造方法(参数)

四.线程池的运行过程(工作原理)

1.当线程数<核心线程数时

2.当线程数=核心线程数且阻塞队列未满时

3.当线程数=核心线程数且阻塞队列满了时

4.当线程数=最大线程数

五.线程池的种类

1.newFixedThreadPool(固定大小的线程池)

2.newCachedThreadPool(缓存线程池)

3.newSingleThreadExecutor

六.线程池中的一些操作方法

1.提交方法

2.关闭线程池


绪论

为什么使用线程池?

        其实,这个问题可以反过来思考一下,不使用线程池会怎么样?当需要多线程并发执行任务时,只能不断的通过new Thread创建线程,每创建一个线程都需要在堆上分配内存空间,同时需要分配虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间,当这个线程对象被可达性分析算法标记为不可用时被GC回收,这样频繁的创建和回收需要大量的额外开销。再者说,JVM的内存资源是有限的,如果系统中大量的创建线程对象,JVM很可能直接抛出OutOfMemoryError异常,还有大量的线程去竞争CPU会产生其他的性能开销,更多的线程反而会降低性能,所以必须要限制线程数。

既然不使用线程池有那么多问题,我们来看一下使用线程池有哪些好处:

  • 使用线程池可以复用池中的线程,不需要每次都创建新线程,减少创建和销毁线程的开销;
  • 同时,线程池具有队列缓冲策略、拒绝机制和动态管理线程个数,特定的线程池还具有定时执行、周期执行功能,比较重要的一点是线程池可实现线程环境的隔离,例如分别定义支付功能相关线程池和优惠券功能相关线程池,当其中一个运行有问题时不会影响另一个。

一.Java线程池之间的继承关系

其中ExecutorService是底层的接口,而ScheduledExecutorService则扩展了一些方法,使得其实现ScheduledThreadPoolExecutor(任务调度线程池)可以让线程延迟执行任务。

二.线程池的状态

ThreadPoolExxcutor使用int的高3位来表示线程池的状态,低29位表示线程数量

 细节:为什么要用一个int来同时保存线程池的状态和线程数量呢?

答:因为线程池的状态和其线程的数量同时保存在一个原子变量,这样就可以用一次cas原子操作就可以为其赋值,保证了原子性。

三.线程池的构造方法(参数)

线程池的构造方法参数有:

public ThreadPoolExecutor(
     int corePoolSize,
     int maximumPoolSize,
     long keepAliveTime,
     TimeUnit unit,
     BlockingQueue<Runnable> workQueue,
     ThreadFactory threadFactory,
     RejectedExecutionHandler handler)
  • corePoolSize:核心线程数,最多保留的线程数
  • maximumPoolSize:最大线程数目(核心线程数+急救线程数)
  • keepAliveTime:急救线程的生存时间
  • unit:生存时间的时间单位
  • workQueue:阻塞队列(当线程数等于核心线程数,则会将任务加入阻塞队列。当阻塞队列也满了,则会申请救急线程来执行多出来的任务)
  • threadFactory:线程工厂(主要是对线程的创建进行一些定义,如命名)
  • handler:拒绝策略(当线程数等于最大线程数,此时还需要额外的线程时,则会触发拒绝策略。常见的拒绝策略有:调用者抛出RejectedExecutionException异常、调用者执行本次任务、放弃本次任务、放弃队列中最早的任务,本任务取而代之、Netty则会创建一个新线程来执行)

四.线程池的运行过程(工作原理)

1.当线程数<核心线程数

        此时线程池会继续创建线程来执行任务

2.当线程数=核心线程数且阻塞队列未满

        此时线程池会将待执行的任务放入阻塞队列中,直到核心线程空闲后,从队列中取出任务执行。

3.当线程数=核心线程数且阻塞队列满了

        此时将会创建不超过maximumPoolSize-corePoolSize数目的救急线程来执行多出的任务(有点插队的感觉,堵在阻塞队列外面的反而不用排队,直接由救急线程处理)。

4.当线程数=最大线程数

        此时如果有新的任务需要执行,则会执行拒绝策略。

五.线程池的种类

JDK Executors类中提供了众多工厂方法来创建各种用途的线程池

1.newFixedThreadPool(固定大小的线程池)

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(
        nThreads, nThreads,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>());
}

特点

  • 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间
  • 阻塞队列是无界的,可以放任意数量的任务
  • 适用于任务量已知,相对耗时的任务

2.newCachedThreadPool(缓存线程池)

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(
        0, Integer.MAX_VALUE,
        60L, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>());
}

特点

  • 核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着
    • 全部都是救急线程(60s 后可以回收)
    • 救急线程可以无限创建(容易造成内存溢出)
  • 队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)
  • 适合任务数比较密集,但每个任务执行时间较短的情况

3.newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService(
        new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, 
                                new LinkedBlockingQueue<Runnable>()));
}

使用场景

        希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程 也不会被释放。

区别

        自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一 个线程,保证池的正常工作。

        Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改 FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法

        Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改 对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改

细节

        newSingleThreadExecutor中的阻塞队列是无界限的队列

六.线程池中的一些操作方法

1.提交方法

// 执行任务
void execute(Runnable command);

// 提交任务 task,用返回值 Future 获得任务执行结果
<T> Future<T> submit(Callable<T> task);

// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
 throws InterruptedException;

// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                              long timeout, TimeUnit unit)
 throws InterruptedException;

// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
 throws InterruptedException, ExecutionException;

// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
                long timeout, TimeUnit unit)
 throws InterruptedException, ExecutionException, TimeoutException;

2.关闭线程池

/*
线程池状态变为 SHUTDOWN
- 不会接收新任务
- 但已提交任务会执行完
- 此方法不会阻塞调用线程的执行
*/
void shutdown();

/*
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用 interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow()

// 不在 RUNNING 状态的线程池,此方法就返回 true
boolean isShutdown();

// 线程池状态是否是 TERMINATED
boolean isTerminated();

// 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值