文章目录

  • 一、线程池
  • 二、线程池的优势
  • 三、什么时候使用线程池?
  • 四、线程池架构图
  • 五、线程池的五种状态
  • 六、ThreadPoolExecutor默认线程池
  • 七、线程池七大参数
  • 八、线程池的工作原理
  • 九、线程池抛异常会如何?
  • 十、execute的执行流程
  • 十一、ScheduledThreadPoolExecutor定时线程池
  • 十二、定时线程池的三种任务提交方式,以及区别


一、线程池

“线程池”,顾名思义就是一个线程缓存,线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此Java中提供线程池对线程进行统一分配、调优和监控

二、线程池的优势

  • 1.线程复用,减少线程创建、销毁的开销,提高性能
  • 2.提高响应速度,当任务到达时,无需等待线程创建就能立即执行
  • 3.提高线程的可管理性。使用线程池可以进行统一的分配,调优和监控。

三、什么时候使用线程池?

  • 1.单个任务处理时间比较短
  • 2.需要处理的任务数量很大

四、线程池架构图

ThreadPoolExecutor:普通线程池

ScheduledThreadPoolExecutor:定时线程池

java线程池的queue选哪个 java中的线程池问题_java


图中可以看出,我们使用的线程池都实现了ExecutorService和Executor两个接口,他们定义了线程池的各种行为!

  • 1.execute(Runnable command):执行Runnable类型的任务
  • 2.submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
  • 3.shutdown():温柔的关闭线程池,停止接受新任务,并执行完未完成的任务。
  • 4.shutdownNow():强制关闭线程池,未完成的任务会以列表形式返回
  • 5.isTerminated():返回所有任务是否执行完毕。当调用shutdown()方法后,所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true
  • 6.isShutdown():返回的线程池是否关闭,当调用shutdown()或者shutdownNow()方法后返回为true

五、线程池的五种状态

//ctl是线程池的重要属性,这个AtomicInteger通过高三位和低29位非常巧妙的记录了线程的状
态和线程数量!
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
// Integer的低29位存储线程池的线程容量大小
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// Integer的高三位存储线程池的状态
private static final int RUNNING = -1 << COUNT_BITS; // 高 3 位 为 111
private static final int SHUTDOWN = 0 << COUNT_BITS; // 高 3 位 为 000
private static final int STOP = 1 << COUNT_BITS; // 高 3 位 为 001
private static final int TIDYING = 2 << COUNT_BITS; // 高 3 位 为 010
private static final int TERMINATED = 3 << COUNT_BITS; //高3位为011

状态

状态说明

切换条件

RUNNING

运行状态,可以接受任务、处理任务

线程池一旦被创建,就处于RUNNING状态

SHUTDOWN

SHUTDOWN状态,不接收新任务,但能处理已添加的任务

调用线程池的showdown()接口时,线程池由RUNNING ->SHUTDOWN

STOP

STOP状态,不接受新任务,不处理已添加的任务,并且会中断正在处理的任务

调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN)-> STOP

TIDYING

当所有任务已终止,ctl记录的“任务数量”为0,线程池会变为TIDYING状态,此时会执行函数terminated(),用户可自定义其实现。

1.当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由SHUTDOWN ->TIDYING。2.当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP ->TIDYING.

TERMINATED

线程池彻底终止,就变成TERMINATED状态

线程池处在TIDYING状态时,执行完terminated()之后,就会由TIDYING -> TERMINATED

进入TERMINATED状态的条件如下:

  • 1.线程池不是RUNNING状态
  • 2.线程池状态不是TIDYING或TERMINATED状态
  • 3.如果线程池状态是SHUTDOWN并且workerQueue为空;
  • 4.workerCount为0
  • 5.设置TIDYING状态成功

java线程池的queue选哪个 java中的线程池问题_java_02

六、ThreadPoolExecutor默认线程池

ThreadPoolExecutor是Executor底下的实现类,开发中常用的线程池就是它

public class ThreadPoolTest {
    //Task任务类
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"执行任务!");
        }
    }

    public static void main(String[] args) {
        //定义线程池
        ThreadPoolExecutor poolExecutor = new
                ThreadPoolExecutor(2,
                6,
                5,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        //10个线程分别执行Task任务
        for (int i = 0; i < 10; i++) {
            poolExecutor.execute(new Task());
        }
    }
}

七、线程池七大参数

  • 1.corePoolSize核心线程数,当提交一个任务时,线程池会创建一个新线程执行任务,此时线程不会复用。如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。此时如果有核心线程有空闲,回去阻塞队列中领取任务,此时核心线程复用。
  • 2.maxmumPoolSize最大线程数,线程池中允许的最大线程数,如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maxmumPoolSize
  • 3.keepAliveTime超时时间,当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待。直到等待的时间超过了keepAliveTime才会被销毁,最终会收缩到corePoolSzie的大小。
  • 4.unit超时时间单位,keepAliveTime的时间单位
  • 5.workQueue阻塞队列,用来保存等待被执行的任务的阻塞队列,且任务必须实现Runnable接口,在JDK中提供了如下阻塞队列:
  • ①ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO(先进先出)排序任务。
  • ②LinkedBlockingQueue:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQueue
  • ③SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐1量通常要高于LinkedBlockingQueue
  • ④priorityBlockingQueue具有优先级的无界阻塞队列
  • 6.threadFactory线程工厂,他是ThreadFactory类型的变量,用来创建新线程。默认使用Executors.defaultThreadFactory()来创建线程。
  • 7.handler拒绝策略,线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略
  • ①AbortPolicy:直接抛出异常,默认策略
  • ②CallerRunsPolicy:用调用者所在的线程来执行任务
  • ③DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务
  • ④DiscardPolicy:直接丢弃任务

八、线程池的工作原理

  • 1.在创建了线程池后,开始等待请求
  • 2.当调用execute()方法添加一个请求任务时,线程池会做以下判断:
  • ①如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
  • ②如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
  • ③如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务
  • ④如果队列满了且正在运行的线程数量大于或者等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
  • 3.当一个线程完成任务时,他会从队列中取下一个任务来执行
  • 4.当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
  • ①如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉
  • ②所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小

九、线程池抛异常会如何?

如果线程池中执行时,其中一个线程出现异常,是不会影响其他线程执行的。在线程池内部如果其中一个线程出现异常,后面的线程任然可以执行。

十、execute的执行流程

java线程池的queue选哪个 java中的线程池问题_多线程_03

十一、ScheduledThreadPoolExecutor定时线程池

用于处理延时执行任务,或间隔时间循环执行任务的场景,定时线程池ScheduledThreadPoolEcecutor没有非核心线程的概念,都是核心线程,这是他与默认线程池的一点区别。

十二、定时线程池的三种任务提交方式,以及区别

  • ①schedule
  • ②scheduledAtFixedRate:可能会堆积任务以至于OOM
  • ③scheduledWithFixedDelay
    三种提交方式的使用和区别如下代码所示:
//定义一个延时线程池
        ScheduledExecutorService scheduledThreadPool =
                Executors.newScheduledThreadPool(5);
        //延时线程池
        ScheduledFuture<?> schedule = scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("延时线程池,延迟三秒执行!");
            }
        },3,TimeUnit.SECONDS);
        //延时线程池
        ScheduledFuture<?> scheduled2 = scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("延时线程池,延迟一秒执行,不等待上一个任务结束,每三秒执行一次!");
            }
        },1,3,TimeUnit.SECONDS);
        //延时线程池
        ScheduledFuture<?> scheduled3 = scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟线程池,延迟一秒执行,然后等待上一个任务结束,再每三秒执行一次");
            }
        },1,3,TimeUnit.SECONDS);