栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

(多)线程应用 | JAVA中线程池的运用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(多)线程应用 | JAVA中线程池的运用

1.线程池参数介绍

这里先粘贴一段ThreadPoolExecutor的源代码:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  1. corePoolSize:核心线程数

    • 核心线程会一直存活,及时没有任务需要执行
    • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

    如果不知道怎么写,按照阿里工程师的写法就可以
    int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();

  2. maximumPoolSize:最大线程数
    当线程池中的线程数等于 corePoolSize 且 workQueue 已满,这时就要看当前线程数是否大于 maximumPoolSize,如果小于maximumPoolSize 定义的值,则会继续创建线程去执行任务, 否则将会调用去相应的任务拒绝策略来拒绝这个任务

  3. keepAliveTime:线程空闲时间

超过 corePoolSize的线程被称做"Idle Thread", 这部分线程会有一个最大空闲存活时间(keepAliveTime),如果超过这个空闲存活时间还没有任务被分配,则会将这部分线程进行回收,直到线程数量=corePoolSize

  1. unit:空闲时间单位
    TimeUnit枚举类中有如下七个参数可选用
TimeUnit.DAYS;              //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
  1. workQueue:任务队列容量(阻塞队列)
    如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到该队列当中,只要超过 corePoolSize 就会把任务添加到该缓存队列,(不是一定可以添加成功),如果成功的话就会等待空闲线程去执行该任务,若添加失败(一般是队列已满),就会根据当前线程池的状态决定如何处理该任务:
    1. 若线程数 < maximumPoolSize 则新建线程;
    2. 若线程数 >= maximumPoolSize,则会根据拒绝策略做具体处理)。

阻塞队列创建:
1 ArrayBlockingQueue       //基于数组的先进先出队列,此队列创建时必须指定大小;
2 linkedBlockingQueue      //基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
3 synchronousQueue        //这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

  1. threadFactory:线程工厂
    用来为线程池创建线程,当不指定线程工厂时,默认调用Executors.defaultThreadFactory()创建默认线程工厂,其后续创建的线程优先级都是Thread.NORM_PRIORITY。如果我们指定线程工厂,我们可以对产生的线程进行一定的操作。

  2. handler:任务拒绝处理器
    当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务,默认是AbortPolicy,会抛出异常。

四种拒绝策略:

ThreadPoolExecutor.AbortPolicy:         // 丢弃任务并抛出RejectedExecutionException异>常。
ThreadPoolExecutor.DiscardPolicy:       // 也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:    // 丢弃队列最前面的任务,然后重新尝试执行任务>(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:      // 由调用线程处理该任务


2.线程池运用 2.1. 创建线程池

每项参数参考设置如下:

 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                Runtime.getRuntime().availableProcessors() * 2,
                Runtime.getRuntime().availableProcessors() * 3,
                30,
                TimeUnit.SECONDS,
                new SynchronousQueue(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

2.2. 调用

可以使用execute和submit两个方法向线程池提交任务

2.2.1 execute

execute方法用于提交不需要返回值的任务,利用这种方式提交的任务无法得知是否正常执行

threadPoolExecutor.execute(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
2.2.2 submit

submit方法用于提交一个任务并带有返回值,这个方法将返回一个Future类型对象。可以通过这个返回对象判断任务是否执行成功,并且可以通过future.get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成。

Future future=threadPoolExecutor.submit(futureTask);	
Object value=future.get();
2.3 关闭线程池

可以通过调用ThreadPoolExecutor的shutdown()或shutdownNow()方法来关闭线程池。

方法原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt()方法来中断线程,所以无响应中断的任务可能永远无法停止。但是他们存在一定的区别,shutdownNow()首先将线程池的状态设置为STOP,然后尝试停止所有正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown()只是将线程池的状态设置成SHUTDOWN状态,然后中断所有正在执行的任务。

  • 只要调用了这两个关闭方法的一个,isShutdown就会返回true。
  • 当所有的任务都关闭后,才表示线程池关闭成功,这时调用isTerminated方法会返回true。
  • 至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定执行完,则可以调用shutdownNow方法。
3. 线程池资源分配技巧

参考:@小小旭GISer

要想合理地配置线程池,首先要分析任务特性

阅读下面内容前首先需要知道:
什么是CPU密集型、IO密集型?

  1. 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。

    • CPU密集型任务应该配置尽可能少的线程,如配置N+1个线程,N位CPU的个数。(Thread类有,上方有提到)
    • 而IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*N。
    • 混合型任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量
  2. 任务的优先级:高、中和低。

    • 优先级不同的任务可以交给优先级队列PriorityBlcokingQueue来处理。
  3. 任务的执行时间:长、中和短。

    • 执行时间不同的任务可以交给不同规模的线程池来处理。
  4. 任务的依赖性:是否依赖其他系统资源,如数据库连接。

    • 依赖数据库的任务,因此线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间越长,那么线程数应该设置的越大,这样能更好滴利用CPU。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/270555.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号