zoukankan      html  css  js  c++  java
  • Java线程池原理

    一、引入背景

    1. 线程频繁的创建和销毁会消耗大量系统资源

    2. 线程上线文切换会消耗大量系统资源

    3. 线程数量太多,栈内存会溢出,因为每个线程都有自己的栈

    4. 需要一种机制,可以线程复用,执行完一个任务后不销毁,继续执行其他任务

    5. 还可以提高线程的可控性

    二、线程池定义

    1. 接口关系

    a. Executor接口,Excutor框架把任务的提交和执行进行解耦,只声明了一个方法execute()

    b. ExecutorService: 继承Executor接口,添加了一些用来管理线程的方法

    c. AbstractExecutorService:抽象类,实现了ExecutorService接口

    d. ThreadPoolExecutor类: ExecutorService的默认实现,是线程池中最核心的类

    e. 继承关系:ThreadPoolExecutor->AbstractExecutorService->ExecutorService->Executor

    2. ThreadPoolExecutor类构造方法的源码(重要!!!需要透彻理解)

    public ThreadPoolExecutor(
    
                int corePoolSize, //线程池大小,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
    
                int maximumPoolSize, //线程池能创建的最大线程数,超出corePoolSize和workerCount后的补救措施;超过maximumPoolSize进行拒绝
    
                long keepAliveTime, //表示线程没有任务执行时(即在缓存队列中)最多保持多久时间会终止,当线程数大于corePoolSize才起作用,如果一个线程的空闲时间达到keepAliveTime,则会终止
    
                TimeUnit unit, //参数keepAliveTime的时间单位
    
                BlockingQueue<Runnable> workQueue, //一个阻塞队列,用来存储等待执行的任务,此时线程被阻塞,当workQueue已满,并且corePoolSize<workerCount<maximumPoolSize时,创建线程执行任务;当workerCount>maximumPoolSize时,拒绝

    ThreadFactory threadFactory, //线程工厂,主要用来创建线程,可以对线程命名

    RejectedExecutionHandler handler //表示拒绝处理任务时的策略,主要是丢弃任务 );
    3. ThreadPoolExecutor类的核心方法:
    a. execute(Runnable)没有返回值,也是线程池执行的底层原理
    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            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);
    }
     
     
    b. submit(Callable)有返回值,跟Future相关,底层还是调用execute()方法
     
    c. 线程池的关闭 shutdown()和shutdownNow()
    shutdown() 不接受新的任务
    shutdownNow() 不接受新的任务,并尝试终止正在运行的线程

    d. 线程池容量的动态调整 setCorePoolSize()和setMaximumPoolSize()

     
     
    三、实现原理(重要!!!需要透彻理解)
    1. 线程池状态
    private static final int RUNNING    = -1 << COUNT_BITS; //正常运行状态
    private static final int SHUTDOWN   =  0 << COUNT_BITS; //shutdown()方法,不再接受新任务
    private static final int STOP       =  1 << COUNT_BITS; //shutdownNow()方法,不再接受新任务,并尝试终止正在执行的任务
    private static final int TIDYING    =  2 << COUNT_BITS; //所有任务都执行完毕
    private static final int TERMINATED =  3 << COUNT_BITS; //terminated()方法,终止状态

     
    2. 线程池中的线程初始化
    • prestartCoreThread():初始化一个核心线程;
    • prestartAllCoreThreads():初始化所有核心线程

    3. 任务缓存队列及排队策略, 如果超过了corePoolSize,会缓存线程到workQueue

    4. 任务拒绝策略,如果超出了maxPoolSize,会拒绝任务并丢弃

     

    四、创建线程池:

    1. Executors的4个静态方法,底层实现都是ThreadPoolExecutor

    newSingleThreadExecutor       创建只有一个线程的线程池

    newFixedThreadPool      创建线程数量固定的线程池

    newCachedThreadPool          创建可缓存的线程池,线程数量无限制

    newScheduledThreadPool  创建周期性执行任务的线程池,线程数量限制

    2. 直接创建ThreadPoolExecutor对象

     
    五、示例代码
    ExecutorService executorService = Executors.newFixedThreadPool(20); 

    Executor executor=Executors.newFixedThreadPool(20);
    executorService.execute(() -> { // code });
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(workQueueCount),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
    ExecutorService executorService = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(workQueueCount),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
    threadPoolExecutor.execute(()->{ // code });
     
     六、如何配置线程池
    1. IO密集型任务:由于线程并不是一直在运行,都在等待IO,所以尽可能多配置线程,比如CPU个数*2
    2. CPU密集型任务:分配较少的线程,比如CPU个数
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    RAID磁盘阵列详解以及软RAID的实施部署
    Ubuntu 安装 配置 Mysql
    Asp.net页面之间传递参数的几种方法
    asp.net中网页间传递参数用什么方法比较
    Asp.net页面之间传递参数的几种方法
    asp.net中网页间传递参数用什么方法比较
    Asp.net页面之间传递参数的几种方法
    asp.net中网页间传递参数用什么方法比较
    CSS2.0中最常用的18条技巧
    Asp.net页面之间传递参数的几种方法
  • 原文地址:https://www.cnblogs.com/june0816/p/6140513.html
Copyright © 2011-2022 走看看