zoukankan      html  css  js  c++  java
  • 并发编程之:线程池(一)

    大家好,我是小黑,一个在互联网苟且偷生的农民工。

    池化

    线程池是在计算机开发中常见的一种池化技术,是为了提高资源的利用率,将一些资源重复利用,避免重复的构建来提高效率。类似字符串常量池,数据库连接池,HttpClient连接池等,都是用的池化技术。

    线程池

    在没有线程池概念之前,我们要使用线程必须先通过创建一个Thread类来完成线程的构建,并调用start()方法开启,在线程执行完会将线程销毁,而线程资源是很宝贵的,创建和销毁线程会造成资源的浪费。而线程池是将创建的线程存储到一个池中,在需要使用时从池中去拿,使用完之后再讲线程归还到池中,下一次接着使用。

    举个栗子,好比我们去银行办理业务时,银行会有窗口为客户办理业务,如果没有线程池,就好比每次来一个客户,银行都打开一个窗口,办理完业务之后将窗口关闭,这样确实很浪费时间,所以银行会默认开几个窗口,比如三个,等客户来办理业务;一个客户办理完,下一个客户可以继续在这个窗口办理。

    核心线程数

    在线程池初始化时,会指定创建核心线程的数量,有任务提交给线程池时,先判断是否有空闲线程,如果有空闲线程,则直接使用,如果没有则看当前线程池中的数量是不是小于核心线程数,如果是则创建新的线程,如果已经到达核心线程数,则需要做下一步操作。

    等待队列

    下一步操作就是要进入等待队列,等待队列好比是去银行办业务时没有空闲窗口,需要坐在大厅的座椅上排队;线程池也是一样,如果没有核心线程,则需要将任务放入等待队列,等待有空闲线程再执行。

    最大线程数

    那我们都知道银行有时候人特别多的时候,会增加窗口,一般是当大厅的座椅坐满人时,窗口都很紧张处理不过来,这是会增加窗口;线程池也是一样,如果等待的任务已经放满了等待队列,并且核心线程都在繁忙,这时会查看线程池中的线程数量是否到达最大线程数,如果没有则会创建新的线程来继续处理。

    image-20210903211946836

    拒绝策略

    那如果说,线程池中的线程已经到达最大线程数并且都在繁忙,还有新的任务进来,好比银行已经坐满人了,窗口也都在忙,客户都排到门口了,这时要是还有人要办理,应该怎么处理呢?银行一般会让这个人先回家,改天再来办,或者如果是个大客户,银行可能会单独带去VIP办公室办理等等。线程池在这种情况下也有相应的处理方式,这种处理方式我们称之为拒绝策略,如果会放任务在当前线程执行,或者直接将任务丢弃等等,在后面的章节中我会详细给大家介绍。

    JDK中的线程池

    在JDK中提供了相应的API来创建线程池,这些API也是在我们最近讲到过的JUC包中。

    image-20210903212843343

    Executor

    首先,线程池需要具备能够执行任务的能力,这个任务通过一个线程来处理。而这个执行任务的能力通过Executor接口来约定。

    public interface Executor {
        void execute(Runnable command);
    }
    

    ExecutorService

    在某些场景我们需要知道任务执行完之后的结果,拿到返回值,而Runnable接口是没有返回值的;以及一些可以关闭线程池中的线程,执行线程中的任务的方法,定义在ExecutorService接口中。

    image-20210903221251283

    ThreadPoolExecutor

    ThreaPoolExecutor则是对ExecutorService的具体实现类,通过ThreaPoolExecutor类可以创建出一个线程池,我们先来看一下代码。

    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        // 核心线程数 corePoolSize
        3, 
        // 最大线程数 maximumPoolSize
        5, 
        // 空闲线程保留存活的时间和时间单位
        10, TimeUnit.SECONDS, 
        // 等待队列
        new ArrayBlockingQueue<>(3), 
        // 创建线程的工厂
        Executors.defaultThreadFactory(), 
        // 拒绝策略
        new ThreadPoolExecutor.AbortPolicy() 
    );
    

    从代码我们可以看出,创建一个线程池,需要指定我们上面说到的核心线程数,最大线程数,等待队列,拒绝策略等,并且还要指定创建线程的工厂对象。

    当然ThreadPoolExecutor也有其他的构造方法,可以不显式指定拒绝策略和工厂对象。

    new ThreadPoolExecutor(3,5,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3));
    // 构造方法
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        // 使用默认的线程工厂和默认的拒绝策略。
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler); 
    }
    

    7个线程池参数

    • corePoolSize:核心线程数
    • maximumPoolSize:最大线程数
    • keepAliveTime:空闲线程保持存活时间
    • unit:空闲线程保持存活时间单位
    • workQueue:等待队列
    • threadFactory:线程创建工厂
    • RejectedExecutionHandler:拒绝策略

    4种拒绝策略

    这里我们说一下4中拒绝策略。接口RejectedExecutionHandler定义了拒绝策略,所有的拒绝策略都需要实现该接口。

    public interface RejectedExecutionHandler {
        void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    }
    

    在ThreadPoolExecutor类中定义了4个拒绝策略的具体实现。

    • AbortPolicy:拒绝处理,抛出异常
    • CallerRunsPolicy:由创建该线程的线程(main)执行
    • DiscardPolicy: 丢弃,不抛出异常
    • DiscardOldestPolicy:和最早创建的线程进行竞争,不抛出异常

    可通过如下方式进行拒绝策略的创建。

    // 拒绝处理,抛出异常
    new ThreadPoolExecutor.AbortPolicy(); 
    // 由创建该线程的线程(main)执行
    new ThreadPoolExecutor.CallerRunsPolicy();  
    // 丢弃,不抛出异常
    new ThreadPoolExecutor.DiscardPolicy(); 
    // 和最早创建的线程进行竞争,不抛出异常
    new ThreadPoolExecutor.DiscardOldestPolicy(); 
    

    4种线程池种类

    那么具体我们在使用时应该创建怎样的线程池呢?在JDK的Executors工具类为我们提供了4种线程池的创建方式。

    // 只有一个线程
    Executors.newSingleThreadExecutor(); 
    // 固定线程数
    Executors.newFixedThreadPool(5); 
    // 可伸缩的
    Executors.newCachedThreadPool(); 
    // 可延迟执行,使用优先队列DelayedWorkQueue
    Executors.newScheduledThreadPool(3);
    

    小结

    好的,通过今天的内容我们先对线程池的使用有一个初步的了解,下期内容再跟大家深入解析一下线程池中的具体实现原理。

    本期内容就到这里,我们下期见。
    关注公众号【小黑说Java】干货不断。
    image

  • 相关阅读:
    饮冰三年-人工智能-Python-35权限管理(万能的权限通用模块)
    饮冰三年-人工智能-Python-34CRM项目实战
    饮冰三年-人工智能-Python-33权限管理(通过配置快速生成列表)
    集腋成裘-15-MongoDB系列-02Find详细
    集腋成裘-14-MongoDB系列-01初识MongoDB
    饮冰三年-人工智能-Python-33博客园山寨版之报障管理
    饮冰三年-人工智能-Python-32博客园山寨版之后台管理
    JVM内存管理
    连接池实现原理
    定时器
  • 原文地址:https://www.cnblogs.com/heiz123/p/15225361.html
Copyright © 2011-2022 走看看