并发编程 -- 线程池

一,线程池

java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池

好处:

  1. 降低资源消耗,通过重复利用已经创建的线程来降低线程创建和销毁造成的消耗

  2. 提高响应速度,当任务到达时,可以省去线程创建的时间

  3. 提高线程的可管理性

二,线程池工作及框架

工作流程图:

在这里插入图片描述

Executor框架

1.两级调度模型

java程序通常会把应用分为若干个任务,然后使用调度器Executor将任务映射成固定数量的线程,在底层,操作系统内核将这些线程映射到各个硬件处理器上

在这里插入图片描述

2.框架结构
框架结构

框架成员

框架工作流程

实例:

@Slf4j
public class ExecutorTest {

    /**
     * callable测试内部类
     */
    static class CallableTest implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            return 1;
        }
    }

    /**
     * runnable测试内部类
     */
    static class RunnableTest implements Runnable{
        @Override
        public void run() {
            log.info("runnable ........");
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //构造线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10,
                100, MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));

        //提交并执行runnable任务
        threadPoolExecutor.execute(new RunnableTest());

        //提交并执行callable任务,获取返回的Future对象
        Future<Integer> future = threadPoolExecutor.submit(new CallableTest());
        //通过该对象获取返回值
        Integer i = future.get();
        log.info("i ---------->"+i);
    }
}

三,线程池实现

ThreadPoolExecutor

Executor框架最核心的类就是ThreadPoolExecutor,他是线程池的实现类

构成组件

  1. corePool:核心线程池大小

  2. maximumPool:最大线程池大小

  3. BlockingQueue:用来暂时保存的任务的任务队列

  4. RejectedExecutionHandler:当ThreadPoolExecutor关闭或者ThreadPoolExecutor饱和时对任务的处理策略

手动创建线程池

    /**
     * @Param: corePoolSize : 核心线程数
     * @Param:maximumPoolSize:最大线程数
     * @Param:keepAliveTime:空闲线程等待新任务的最长时间
     * @Param:unit:计数单位
     * @Param:workQueue:任务队列
     */
    //ThreadPoolExecutor tpe = new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize
    //                              ,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);


    /**
     * 手动创建线程池
     * @return
     */
    public ExecutorService executorService(){
        return new ThreadPoolExecutor(5, 10,
                100, MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
    }
FixedThreadPool

FixedThreadPool是固定线程数的线程池

    /**
     * 创建 FixedThreadPool
     * @return
     */
    public ExecutorService fixedThreadPool(){
        return Executors.newFixedThreadPool(8);
    }

源码:

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

分析:

  • 使用无界的LinkedBlockingQueue来作为任务队列,容量为Integer.MAX_VALUE带来的影响

    • 当线程池中的线程数达到corePoolSize时,任务将进入任务队列等待,此时,使用LinkedBlockingQueue不会拒绝任务,任务会源源不断的进入任务队列中
  • keepAliveTime是0,意味着多余的空闲线程会被立即停止

  • FixedThreadPool是线程数量固定的线程池,适用于为了满足资源管理的需求,而需要适当限制当前线程数量的情景,适用于负载比较重的服务器

SingleThreadExecutor

singleThreadExecutor是使用单个线程的Executor

    /**
     * 创建SingleThreadExecutor
     * @return
     */
    public ExecutorService singleThreadExecutor(){
        return Executors.newSingleThreadExecutor();
    }

源码

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

源码分析

  • corePoolSize = 1线程在执行完一个任务后,会在一个循环总从无界的任务队列中反复取一个任务执行

  • 使用的还是无界队列,影响如上

  • SingleThreadExecutor是只有一个线程的线程池,常用于需要让线程顺序执行,并且在任意时间,只能有一个任务被执行,而不能有多个线程同时执行的场景。

CachedThreadPool

CachedThreadPool是一个会根据需要创建新线程的线程池

    /**
     * 创建CachedThreadPool
     * @return
     */
    public ExecutorService cachedThreadPool(){
        return Executors.newCachedThreadPool();
    }

源码:

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

源码分析:

  • SynchronizedQueue是不存储元素的阻塞队列,实现“一对一的交付”,也就是说,每次向队列中put一个任务必须等有线程来take这个任务,否则就会一直阻塞该任务,如果一个线程要take一个任务就要一直阻塞知道有任务被put进阻塞队列。

  • 因为maximumPoolSize=Integer.MAX_VALUE,因此可以不断的创建新线程,这样可能会CPU和内存资源耗尽

  • CachedThreadPool适用于执行很多短期异步任务的小程序,或者是负载较轻的服务器。

四,线程池关闭及监控

1.线程池关闭

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。
原理都是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。
当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。
至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法。

    /**
     * 线程池关闭
     * @param executorService
     */
    public void shutDownThreadPool(ExecutorService executorService){
        //遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程
        
        //按过去执行已提交任务的顺序发起一个有序的关闭
        executorService.shutdown();
        //shutdownNow会强制停止所有正在执行的任务
        //executorService.shutdownNow();
    }
}

2.监控线程池

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。
可以通过线程池提供的参数进行监控,在监控线程池的时候可以使用以下属性

  • taskCount:线程池需要执行的任务数量,是个近似值。

  • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount,是个近似值。

  • largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池
    是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。

  • poolSize:线程池当前的线程数总量,包括活动的线程与闲置的线程。

  • activeCount:获取活动的线程数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值