Java并发线程池使用和原理(通俗易懂版)

提示:本文基于上一篇文章继续阐述创建多线程的方式
全文内容为:
1.理解线程池的用法
2.掌握线程池的配置原理
全文文字4666,不多,但都是精华,有案例有内容,请耐心看
本人能力有限,如有遗漏或错误,敬请指正,谢谢


其他文章

1.Java多线程基本概念和常用API(面试高频)
2.线程安全问题(synchronized解决,各种类型全)
3.Java并发线程池使用和原理(通俗易懂版)
4.基于SpringBoot+Async注解整合多线程

前言

学习一门技术最好使用wwh方法
what:这门技术是什么
why:为什么用这个技术,使用会有什么优化
how:怎么使用


提示:以下是本篇文章正文内容,下面案例可供参考

一、线程池的理解

在我们平常的编码中,通常会将一些对象保存起来,这主要考虑的是对象的创建成本。

比如像线程资源、数据库连接资源或者 TCP 连接等,这类对象的初始化通常要花费比较长的时间,如果频繁地申请和销毁,就会耗费大量的系统资源,造成不必要的性能损失。

并且这些对象都有一个显著的特征,就是通过轻量级的重置工作,可以循环、重复地使用。

这个时候,我们就可以使用一个虚拟的池子,将这些资源保存起来,当使用的时候,我们就从池子里快速获取一个即可。

在 Java 中,池化技术应用非常广泛,常见的就有数据库连接池、线程池等,本文主讲线程池

二、使用线程池的好处

2.1 传统方式

使用new Thread()的方式创建线程 意味着每次在使用多线程的地方都要创建一个Thread对象: ①势必有创建及销毁线程耗费资源
②线程上下文切换问题
③同时创建过多的线程也可能引发资源耗尽的风险

最主要的是:不方便管理,在使用new的方式,还需要关心线程是否关闭,是否会耗尽,所以我们需要有个容器进行统一管理。

public static void main(String[] args) {
    System.out.println("主线程开启");
    //创建线程池
    Thread t1=new Thread(()->{
    	System.out.println("分线程开启");
    })
    System.out.println("主线程结束");
}

2.2 使用线程池

1.在需要异步执行任务的时候,不需要等待创建新线程的时间(因为在程序启动的时候,已经创建了大量的空闲线程),直接从线程池“这个容器”中取出线程使用,等待执行任务完,就又重新变回空闲状态,不会死亡。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2.正如上面传统方式的不足:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统 的稳定性,使用线程池可以进行统一的分配,调优和监控

三.线程池快速开始

Java中操作线程池对象解释:

顶级接口:
Executor是一个接口,就一个方法execute(Runnable command)它将任务的提交与任务的执行分离。
这里分离的意思是,原本需要new Thread().start()开启多线程,那么现在只需要使用Executor的实现去使用execute()方法就能开启线程
(没看懂没关系,下面有代码实现)



子接口(重要,通常使用这个接口的实现):
ExecutorService接口 对 Executor 接口进行了扩展,提供了返回 Future 对象,终止,关闭线程池等方法


最常用的实现:ThreadPoolExecutor

所以我们创建线程池只需要创建出ThreadPoolExecutor对象,ExecutorService作为引用 多态形式/或者不使用

1.

3.1 使用Executors工具类(重要)

Executors 是一个工具类,类似于 Collections。提供工厂方法来创建不同类型的线程池
在这里插入图片描述

ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用的固定线程数的线程池 参数:创建线程池中包含线程的数量
返回值:ExecutorService接口,可以用ExecutorService接口的实现类来接收

ExecutorService newSingleThreadExecutor()
创建一个只有一个线程的线程池

3.1.1 使用线程池的步骤

1.用Executors工具类创造ExecutorService对象
2.用ExecutorService对象的execute方法或者submit方法来执行任务
3.用shutdown方法关闭线程池

//创建出固定有两个线程的线程池
ExecutorService es = Executors.newFixedThreadPool(2);


//执行任务:任务使用runnable接口无返回值
es.execute(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "---->" + i);
        }
    }
});

//关闭线程池:
es.shutdown();
//执行任务:任务使用callable接口,可以获取返回值
Future<String> future = es.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                String str="实现callable接口创建任务";
                return str;
            }
        });
//获取返回值
String result=future.get();

3.2 手动new ThreadPoolExecutor对象(推荐使用)

在上面已经阐述了,线程池接口的实现类我们通常使用ThreadPoolExecutor
所以可以不借助工具类Executors,直接new一个线程池对象出来

ThreadPoolExecutor executor=new ThreadPoolExecutor(参数有很多,下面逐个介绍)

提示:下面对参数的解释不懂往后看线程池的原理部分就懂了

corePoolSize:核心线程数,表示这个线程池固定存在这么多线程,不会因为没有任务而空闲下来后,把这个线程删除了。线程池初始化是没有线程的,当有一个任务来的时候,就会初始化给定的线程数

maximumPoolSize:最大线程数,表示这个线程池最大存在的线程有多少个,比如设定的核心线程数是5,那么后面新增的线程叫做非核心线程数,那么也就是说最大线程数的设置如果不和核心线程数相同,最大线程数=核心线程数+非核心线程数(后面新增的)

keepAliveTime:表示超时时间,当非核心线程数如果全部空闲下来了,就会在超时时间内自动中止回收,这里注意,如果corePoolSize=maximumPoolSize,那么超时时间设置也没用,因为此时没有非核心线程数

unit:超时时间的单位(时分秒),以这种形式传参TimeUnit.SECONDS

workQueue:保存任务的队列,有任务进来先看看核心线程数有没有空闲的,有的话直接执行任务了,如果没有空闲的情况,就放进队列中,一般使用LinkedBlockingDeque

threadFactory:创建线程池的工具类,默认使用Executors.defaultThreadFactory(),也可以用guava库的ThreadFactoryBuilder来创建

handler:拒绝策略,意思是当线程池无法继续接收任务了,该把后面进来的任务怎么处理,常见的记一下——①AbortPolicy,再有新来的任务就抛出异常。②DiscardOldestPolicy,抛弃最早未处理的任务。③DiscardPolicy,不处理新任务,直接丢弃(默认)

操作是和上面一样,先创建线程池,然后调用execute()或者submit()方法执行任务

ThreadPoolExecutor executor=new ThreadPoolExecutor(10,50,10,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
        executor.execute(() -> System.out.println("分线程的任务执行"));
        executor.shutdown();

备注:《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式可以明确线程池的运行规则,规避资源耗尽的风险

四、线程池执行任务的原理

1.如果任务数小于coreSize,直接创建新线程并执行(coreSize逻辑)

2.core池满了,后面的任务尝试放入阻塞队列,只要有空闲的core线程,就会执行里面的任务

3.当阻塞队列已经满了,则尝试创建新线程,但数量小于设定的max (maxSize逻辑)

4.如果线程数已经到了max,并且阻塞队列已经满了,后续再有任务进来,就执行RejectHandler

5.当max线程数全部空闲,就会执行keepAlive,到时间后清除max-core的线程(非核心线程,相当于人手不够临时工)

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值