史上最线程池

1. 线程池

1.1 线程池-基本原理

概述 :

​ 提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子,在该池子中存储很多个线程。

线程池存在的意义:

​ 系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系统资源的消耗,这样就有点"舍本逐末"了。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。没有线程池时写多线程是用到线程就创建 ,用完之后消失 浪费操作系统资源

线程池核心原理

1.创建一个池子,池子中是空的
2.提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子
下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
3.但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

线程池工作机制

1.使用线程池 第一次提交任务 先创建线程 用完再还回来

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.第二次提交任务 如果线程池里有线程 就不用创建新线程了 直接用线程池里的线程 用完再还回来

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.如果提交第二个任务 第一个线程还没完成 就创建一个新的线程去执行

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.如果线程池里没有空闲线程 又提交了新的任务 就会创建新的线程去执行外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.线程池可以有上限 自己设置(这里最大线程为3)

三个线程在执行三个任务 后边的就要排队等待

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.2 线程池-Executors默认线程池

概述 : JDK对线程池也进行了相关的实现,在真实企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池。

我们可以使用Executors工具类中所提供的静态方法来创建线程池

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

​ 1.static ExecutorService newCachedThreadPool() 创建一个默认的线程池 没有上限(其实也是有 最大为int的最大值2147483647)
​ 2.static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池

代码实现 :

package com.itheima.mythreadpool;


//static ExecutorService newCachedThreadPool()   创建一个默认的线程池
//static newFixedThreadPool(int nThreads)	    创建一个指定最多线程数量的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {

        //1,创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳int类型的最大值.
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors --- 可以帮助我们创建线程池对象
        //ExecutorService --- 可以帮助我们控制线程池

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        //Thread.sleep(2000);

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        executorService.shutdown();
    }
}

1.3 线程池-Executors创建指定上限的线程池

使用Executors中所提供的静态方法来创建线程池

​ static ExecutorService newFixedThreadPool(int nThreads) : 创建一个指定最多线程数量的线程池

代码实现 :

package com.itheima.mythreadpool;

//static ExecutorService newFixedThreadPool(int nThreads)
//创建一个指定最多线程数量的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //参数不是初始值而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//0

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "在执行了");
        });

        System.out.println(pool.getPoolSize());//2
//        executorService.shutdown();
    }
}

1.4 线程池-ThreadPoolExecutor

如果想修改排队的长度等参数设置 可以不用工具类自己自定义创建线程池对象

创建线程池对象 :

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间(值),空闲线程最大存活时间(单位),任务队列,创建线程工厂,任务的拒绝策略);

线程池工作机制

1.核心线程3 临时线程3 此时提交三个任务 创建三个线程处理这三个任务

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.如果提交五个任务 就会创建三个线程处理前三个 后两个排队 等有空余线程后边的才会被执行

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.提交八个任务 先创建三个线程处理前三个123 剩下的任务去排队 如果此时设置队伍长度为3 则456去排队 这时候线程池才会创建临时线程去执行任务78

临时线程创建时机:核心线程都在忙 队伍排满了 此时创建临时线程

所以任务不是按照提交顺序执行的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.提交十个任务 先创建三个线程处理前三个123 任务456去排队 然后创建临时线程执行789 这时有其他任务就会触发任务的拒绝策略 (默认为舍弃不要)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码实现 :

package com.itheima.mythreadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo3 {
//    参数一:核心线程数量
//    参数二:最大线程数 (空闲线程=最大线程-核心线程)
//    参数三:空闲线程最大存活时间
//    参数四:时间单位
//    参数五:任务队列
//    参数六:创建线程工厂
//    参数七:任务的拒绝策略
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        pool.shutdown();
    }
}

15 线程池-参数详解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
corePoolSize:   核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime:  空闲线程最大存活时间,不能小于0
unit:           时间单位
workQueue:      任务队列,不能为null
threadFactory:  创建线程工厂,不能为null      
handler:        任务的拒绝策略,不能为null  

1.6线程池-非默认任务拒绝策略

RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。

ThreadPoolExecutor.AbortPolicy: 		    丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy: 		   丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor.DiscardOldestPolicy:    抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor.CallerRunsPolicy:        调用任务的run()方法绕过线程池直接执行。

注:明确线程池对多可执行的任务数 = 队列容量 + 最大线程数

案例演示1:演示ThreadPoolExecutor.AbortPolicy任务处理策略

public class ThreadPoolExecutorDemo01 {

    public static void main(String[] args) {

        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;

        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-3---->> 执行了任务

控制台报错,仅仅执行了4个任务,有一个任务被丢弃了

案例演示2:演示ThreadPoolExecutor.DiscardPolicy任务处理策略

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;

        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务

控制台没有报错,仅仅执行了4个任务,有一个任务被丢弃了

案例演示3:演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略

public class ThreadPoolExecutorDemo02 {
    public static void main(String[] args) {
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy());
        // 提交5个任务
        for(int x = 0 ; x < 5 ; x++) {
            // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
            final int y = x ;
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
            });     
        }
    }
}

控制台输出结果

pool-1-thread-2---->> 执行了任务2
pool-1-thread-1---->> 执行了任务0
pool-1-thread-3---->> 执行了任务3
pool-1-thread-1---->> 执行了任务4

由于任务1在线程池中等待时间最长,因此任务1被丢弃。

案例演示4:演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略

public class ThreadPoolExecutorDemo04 {
    public static void main(String[] args) {

        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor threadPoolExecutor;
        threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());

        // 提交5个任务
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }
    }
}

控制台输出结果

pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-1---->> 执行了任务
main---->> 执行了任务

通过控制台的输出,我们可以看到次策略没有通过线程池中的线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于快速开发和构建可独立运行的、基于Spring框架的Java应用程序的工具。它可以轻松地创建、配置和管理Spring项目,减少了开发者的配置和部署工作量,使开发人员能够更专注于业务逻辑的实现。 Spring Boot的设计理念是约定优于配置,它提供了一种通过少量的配置文件和注解来进行项目配置的方式。通过自动配置和起步依赖,开发者可以快速搭建一个独立可运行的Spring应用程序。 Spring Boot的主要特点包括以下几个方面: 1. 快速搭建:Spring Boot提供了一系列的起步依赖,可以快速创建一个可运行的应用程序。开发者只需要选择所需功能的起步依赖,Spring Boot就会自动提供对应的配置和依赖。 2. 自动配置:Spring Boot根据项目的依赖和配置情况,自动进行配置。开发者无需手动配置,大部分情况下只需要提供一些关键的配置信息。 3. 内嵌容器:Spring Boot可以将应用程序打包成一个可执行的JAR文件,并且内置了Tomcat、Jetty等多个常用的Web容器,无需单独安装和配置容器。 4. 优化性能:Spring Boot通过使用线程池、缓存、异步处理等技术手段,来优化应用程序的性能。 5. 多数据源支持:Spring Boot支持多个数据源的配置和管理,可以轻松实现多个数据库的读写操作。 总之,Spring Boot具备快速搭建、自动配置、内嵌容器等特点,极大地简化了Spring项目的开发和部署工作量,使得开发者能够更加高效地开发应用程序。它是Spring框架的有力补充,为Java开发者提供了更加便捷的开发方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值