Java自学日记之多线程(四):线程池(基础)

系列文章目录

Java自学日记之多线程(一):线程与进程以及线程的创建
Java自学日记之多线程(二):线程安全以及一些简单的线程同步方法
Java自学日记之多线程(三):阻塞队列



一、线程池的执行流程

在这里插入图片描述
线程池执行所提交的任务过程:

  1. 先判断线程池中核心线程池所有的线程是否都在执行任务。 如果不是,则新创建一个线程执行刚提交的任务,否则,核心线程池中所有的线程都在执行任务,则进入第2步;
  2. 判断当前阻塞队列是否已满,如果未满,则将提交的任务放置在阻塞队列中;否则,则进入第3步;
  3. 判断线程池中所有的线程是否都在执行任务,如果没有,则创建一个新的线程来执行任务,否则,则交给饱和策略进行处理

二、线程池的周期

1.线程池的五种状态

在java.util.concurrent.ThreadPoolExecutor类中,我们能看到线程池被总结出五种状态

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

注释中解释了这五种状态

  • RUNNING: Accept new tasks and process queued tasks (接受新任务并处理排队的任务)
  • SHUTDOWN: Don’t accept new tasks, but process queued tasks
    (不接受新任务,但可以处理排队的任务)
  • STOP: Don’t accept new tasks, don’t process queued tasks, and
    interrupt in-progress tasks (不接受新任务、不处理排队的任务并且中断正在进行的任务)
  • TIDYING: All tasks have terminated, workerCount is zero, the thread
    transitioning to state TIDYING will run the terminated() hook method
    (当所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用 terminated()方法进入TERMINATED 状态。)
  • TERMINATED: terminated() has completed The numerical order among
    these values matters, to allow ordered comparisons. (在terminated()
    方法执行完后进入该状态,表示线程池彻底终止(这串英文注释没读懂,在网上找了个解释。。))

The runState monotonically increases over time, but need not hit each state.
运行状态会随着时间单调增加,但是不需要到达每一个状态

2.线程池各种状态之间的转换

  • RUNNING -> SHUTDOWN
    On invocation of shutdown()
    调用shutdown()函数
  • (RUNNING or SHUTDOWN) -> STOP
    On invocation of shutdownNow()
    调用shutdownNow()函数
  • SHUTDOWN -> TIDYING
    When both queue and pool are empty
    当队列和线程池都为空
  • STOP -> TIDYING
    When pool is empty
    当线程池为空(因为此时队列中的任务已经被中断了)
  • TIDYING -> TERMINATED
    When the terminated() hook method has completed Threads waiting in awaitTermination() will return when the state reaches TERMINATED.
    当terminated()hook方法完成时,等待终止的线程将在状态达到terminated时返回

三、几种常见的线程池

1.ThreadPoolExecutor

(1).newCachedThreadPool

创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程

特征:

  • 线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
  • 线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟)
  • 当线程池中,没有可用线程,会重新创建一个线程

(2).newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。

特征:

  • 线程池中的线程处于一定的量,可以很好的控制线程的并发量
  • 线程可以重复被使用,在显示关闭之前,都将一直存在
  • 超出一定量的线程被提交时候需在队列中等待

(3).newSingleThreadExecutor

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。

特征:

  • 线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行

2.ScheduledThreadPoolExecutor

(1).newSingleThreadScheduledExecutor

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

特征:

  • 线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
  • 可定时或者延迟执行线程活动

(2).newScheduledThreadPool

创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

特征:

  • 线程池中具有指定数量的线程,即便是空线程也将保留
  • 可定时或者延迟执行线程活动

四、线程池的拒绝策略

线程池的拒绝策略有四种

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  • ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛 出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的 任务,然后重新尝试执行任务(重复此过程)
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

五、execute方法

当我们需要向线程池中放入线程时,需要用到execute或submit方法,这里介绍一下excute
excute源码:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

源码里的注释已经很详细了,详细运行规则可以直接看注释

总结一下处理逻辑就是

  • 如果当前运行的线程少于corePoolSize,则会创建新的线程来执行新的任务;
  • 如果运行的线程个数等于或者大于corePoolSize,则会将提交的 任务存放到阻塞队列workQueue中;
  • 如果当前workQueue队列已满的话,则会创建新的线程来执行 任务;
  • 如果线程个数已经超过了maximumPoolSize,则会使用饱和策 略RejectedExecutionHandler来进行处理。

总结

因为今天脑袋有点疼,所以总结的有点草,只想早点写完睡觉了= =
多线程目前就先看到这里吧,更深的知识点等我全学完再回来看吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aftermath123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值