zoukankan      html  css  js  c++  java
  • [置顶] 理解java中的线程池

    1.引入线程池的原因
    对于多线程编程,处理每个请求都要创建一个线程,这不仅要花费时间在创建线程的过程中,还会出现创建线程过多未释放导致的系统内存不足,内存溢出问题,因此引入线程池的概念。线程池,就是在一个容器中创建适量的线程,在程序访问的时候直接调用该线程即可访问。

    2.类比数据库连接池
    数据库连接池与线程池类似,dao层访问数据库时,首先会,加载驱动,建立连接,而每次频繁的建立连接肯定会大大降低系统运行效率,因此,数据库连接池出现了,下面以一张图进行说明:
    这里写图片描述

    如上图,没连接池时访问一次数据库便建立一个Connection,用完,连接释放,添加数据库连接池后,是连接先建立好或者使用时建立,用完,不释放,而是放在数据库连接池中,供下一次请求调用。

    3.线程池的执行流程
    在具体了解线程池之前,有必要先了解一下,线程池的执行流程。
    <1>.当提交一个新任务到线程池中时,先判断线程池中的当前线程数是否大于线程池基本大小(corePoolSize),小于corePoolSize:创建一个新任务来执行当前线程。
    <2>.大于corePoolSize,将任务存放入阻塞队列中(假定此时的阻塞队列是有界的),阻塞队列未满:存放入阻塞队列,等待线程执行。
    <3>队列已满:判断线程池中的线程数量是否大于maximumPoolSize(线程池的最大容量),小于,创建新线程来处理该任务。
    <4>线程池中的线程数量大于maximumPoolSize(线程池的最大容量),即线程池中的所有线程都处于工作状态,此时任务会被拒绝,会交给饱和策略来进行处理。

    4.了解java.util.concurrent包下的ThreadPoolExecutor类
    ThreadPoolExectuor类是线程池的核心类,其可以创建线程池。
    <1> 构造方法(创建线程池时使用):

     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);
    
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit,
                BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory);
    
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit,
                BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler);
    
        public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit,
                BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
                RejectedExecutionHandler handler);

    参数:
    corePoolSize - 池中所保存的线程数,包括空闲线程,线程池中的基本线程数。

    maximumPoolSize - 池中允许的最大线程数。

    keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。即放在阻塞队列中的线程的最大等待时间,

    unit - keepAliveTime 参数的时间单位。

    workQueue - 执行前用于保持任务的阻塞队列。此队列仅保持由 execute 方法提交的 Runnable 任务。

    threadFactory - 执行程序创建新线程时使用的工厂。

    handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序,拒绝执行任务时的策略。

    <2>主要方法:
    1》public void execute(Runnable command):向线程池提交一个任务 command,交由线程池去执行。若执行失败则会调用RejectedExecutionHandler,拒绝执行任务策略进行处理。
    具体源码:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }

    execute方法就是按照上面的线程执行流程来的,

    《2 submit 和execute()一样,用于向线程池提交任务,
    区别:submit提交具有返回值的任务,execute()无返回值。

    《3 shutdown,shutdownNow
    区别:主要在是否关闭正在执行的线程。
    shutdown:逐个遍历线程池中的线程,调用中断处理,中断没有正在执行的线程。
    shudownNow:将线程池的状态设置为stop,停止所有正在执行或停止任务的线程。
    5.几种类与接口间的继承,实现关系图
    类ThreadPoolExecutor继承类AbstractExecutor
    类AbstractExecutor实现接口ExecutorService接口
    ExecutorService接口继承Executor接口

    6.几种具ThreadPoolExecutor
    <1>FixedThreadPool,可重用固定线程数的线程池
    其创建过程调用 ThreadPoolExecutor的构造方法

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    

    其中:
    corePoolSize, maximumPoolSize为一个具体的固定的值。
    keepAliveTime:0L,即多余空闲线程等待新任务的时间为0s,线程池中若存在空闲线程,则会立即被终止。
    这儿阻塞队列,采用了LinkedBlockingQueue无界阻塞队列(队列的最大容量为Integer.MAX_VALUE)。

    采用无界队列带来的影响:
    1.线程池中的正在执行的线程数目不会超过corePoolSize
    原因:超过corePoolSize,便会放在阻塞队列中等待被执行,阻塞队列永远不会满,不会再创建新的线程来执行任务。
    2.maximumPoolSize,线程池中的最大线程数成为无用参数。很好理解。
    3.FixedThreadPool,永远不会拒绝任务,不会使用上面说的几种拒绝策略。(永远不会达到线程池的最大线程数目)

    <2>SingleThreadPool(单个线程池)
    调用ThreadPoolExecutor的构造方法:

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    

    其中:
    corePoolSize, maximumPoolSize 设置为1,使用单个线程。
    keepAliveTime:0L,即多余空闲线程等待新任务的时间为0s,线程池中若存在空闲线程,则会立即被终止。
    这儿阻塞队列,采用了LinkedBlockingQueue无界阻塞队列,单个线程无限反复的从阻塞队列中拿出任务进行执行。
    同样无界队列带来的问题,SingleThreadPool也有。

    <3>.CachedThreadPool(按需分配的线程池)
    调用ThreadPoolExecutor的构造方法:

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    

    其中:
    corePoolSize,默认设置为0,
    maximumPoolSize ,设置Integer.MAX_VALUE,即线程池的最大线程数是无界的。
    keepAliveTime:60L,即多余空闲线程等待新任务的时间为60s,
    这儿阻塞队列,采用了SynchronousQueue无界阻塞队列,SynchronousQueue:一个没有容量的阻塞队列,在插入之前必须有线程从队列中拿走的操作,
    CachedThreadPool:按需分配的线程池,就是主线程offer任务1,线程池中查找有无空闲线程,无则创建新线程让其执行任务1,同时,任务2来了,查看有空闲线程,让其执行任务2,还有空闲线程,等待60s若还没有任务执行,则终止。

    具体实践:
    1.线程池应用之自主实现一个线程池
    2.使用Executors中提供的线程池实现生产者-消费者模型

    参考自:java并发编程的艺术(方腾飞)

  • 相关阅读:
    ES6学习,持续更新!!!
    如何实现图片自适应
    jquery判断元素是否存在
    JS基础_对象字面量
    JS基础_基本数据类型和引用数据类型
    JS基础_属性名和属性值
    JS基础_对象的简介、对象的基本操作
    JS基础_质数练习的改进,提高程序执行效率
    JS基础_break和continue
    JS基础_打印出1-100之间所有的质数
  • 原文地址:https://www.cnblogs.com/wangxiaopei/p/8551179.html
Copyright © 2011-2022 走看看