线程池及线程池的底层工作原理

线程池

1.为什么使用线程池在这里插入图片描述

线程池的优势:

线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从对列中取出任务来执行。

它的主要特点为:线程复用;控制最大并发数;管理线程。
在这里插入图片描述

2.使用 Executors 工具类创建线程池常用的几种方式

方式一:newFixedThreadPool(int)

执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程

package com.test.demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);//银行网点3个窗口
        try {
            for (int i = 1 ; i <= 10;i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 号业务员办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

打印结果:
在这里插入图片描述

方式二:newSingleThreadExecutor()

一个任务一个任务的执行,一池一线程

package com.test.demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool2 = Executors.newSingleThreadExecutor();//银行网点1个窗口
        try {
            for (int i = 1 ; i <= 10;i++){
                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 号业务员办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool2.shutdown();
        }
    }
}

打印结果:(效率低)
在这里插入图片描述

方式三:newCachedThreadPool()

执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建可用时将重用它们,可扩容,遇强则强

package com.test.demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool3 = Executors.newCachedThreadPool();//银行网点可扩展窗口
        try {
            for (int i = 1 ; i <= 30;i++){
                threadPool3.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 号业务员办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool3.shutdown();
        }
    }
}

打印结果:(每次执行结果可能都不一样,此次运行结果只执行了12个线程就完成了30个的任务)
在这里插入图片描述

方式四:newScheduledThreadPool()

创建一个定长线程池,支持定时及周期性任务执行。

package com.test.demo1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ScheduledExecutorService threadPool4 = Executors.newScheduledThreadPool(5);//银行网点可定时窗口
        try {
            for (int i = 1 ; i <= 10;i++){
                threadPool4.schedule(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 号业务员办理业务");
                },3,TimeUnit.SECONDS);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool4.shutdown();
        }
    }
}

执行结果(表示延迟3秒执行):
在这里插入图片描述

3.线程池几个重要参数

3.1 核心底层源码
/**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
     
     /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * {@code newFixedThreadPool(1)} the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
     
     /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory and rejected execution handler.
     * It may be more convenient to use one of the {@link Executors} factory
     * methods instead of this general purpose constructor.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
3.2 七大参数
  • corePoolSize:线程池中的常驻核心线程数
  • maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于等于1
  • keepAliveTime:多余的空闲线程的存活时间当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为止
  • unit:keepAliveTime的单位
  • workQueue:任务队列,被提交但尚未被执行的任务
  • threadFatory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认的即可
  • handler:拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable的策略

4.线程池底层工作原理

4.1 线程底层工作原理图

在这里插入图片描述

4.2 文字说明

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

4.3 在工作中该三种创建线程池的方式都不使用

在这里插入图片描述
FixedThreadPool 和 SingleThreadPool
在这里插入图片描述
CachedThreadPool
在这里插入图片描述

5. 线程池的拒绝策略

5.1 什么情况下使用拒绝策略

等待队列已经排满了,再也塞不下新任务了,同时,线程池中的max线程也达到了,无法继续为新任务服务。这个时候我们就需要拒绝策略机制合理的处理这个问题。

5.2 四种拒绝策略

四种拒绝策略

6.自定义线程池

例子:

package com.test.demo1;

import java.util.concurrent.*;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()//默认的拒绝策略
        );
        try {
            for (int i = 1 ; i <= 9;i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "\t 号业务员办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

打印结果:(当for循环里 i <= 8 时,首先执行两个任务(2个核心线程数),再将三个任务放进阻塞队列中(3个队列的长度),然后当队列中放满时,再根据最大线程数(5个最大线程数)执行剩余的三个任务,当超过核心线程数和一定数量的队列长度且达到最大线程数时,就会报异常(选择的是默认拒绝策略:AbortPolicy)。注:每一次执行的结果可能都一样,有时不会出现报错)
【java.util.concurrent.RejectedExecutionException: Task com.test.demo1.MyThreadPoolDemo$$Lambda 1 / 1078694789 @ 3 b 9 a 45 b 3 r e j e c t e d f r o m j a v a . u t i l . c o n c u r r e n t . T h r e a d P o o l E x e c u t o r @ 7699 a 589 [ R u n n i n g , p o o l s i z e = 5 , a c t i v e t h r e a d s = 0 , q u e u e d t a s k s = 0 , c o m p l e t e d t a s k s = 8 ] a t j a v a . u t i l . c o n c u r r e n t . T h r e a d P o o l E x e c u t o r 1/1078694789@3b9a45b3 rejected from java.util.concurrent.ThreadPoolExecutor@7699a589[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8] at java.util.concurrent.ThreadPoolExecutor 1/1078694789@3b9a45b3rejectedfromjava.util.concurrent.ThreadPoolExecutor@7699a589[Running,poolsize=5,activethreads=0,queuedtasks=0,completedtasks=8]atjava.util.concurrent.ThreadPoolExecutorAbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.test.demo1.MyThreadPoolDemo.main(MyThreadPoolDemo.java:18)】
在这里插入图片描述
拒绝策略模式改为 new ThreadPoolExecutor.CallerRunsPolicy()。
打印的结果为:
在这里插入图片描述
拒绝策略模式改为 new ThreadPoolExecutor.DiscardOldestPolicy()。
打印的结果为:
在这里插入图片描述
拒绝策略模式改为new ThreadPoolExecutor.DiscardPolicy()。
打印的结果为:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值