暑期JAVA学习(36)线程池

线程池

一、线程池概述

(1)什么是线程池?

●线程池就是一个可以复用线程的技术。

(2)不使用线程池的问题

●如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。

(3)线程池原理

●首先会线程池会在内部放一些固定的工作线程,每当来一个任务时,就会生成一个核心线程来处理,再来一个任务,就会再生成一个核心线程来处理。在这里插入图片描述

在这里插入图片描述

●假设线程池控制只能生成三个工作线程,那么此时线程池就已经达到了上限,再进入新任务时就不会产生新的线程,而是已经生成的线程会等当前的任务完成之后,再去依次处理新的任务,从而实现三个线程可以处理很多的任务,避免突然生成很多的线程,严重影响系统的性能。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、线程池实现的API、参数说明

(1)谁代表线程池?

●JDK 5.0起提供了代表线程池的接口:ExecutorService

(2)如何得到线程池对象

●方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

●方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

(3)ThreadPoolExecutor构造器的参数说明

在这里插入图片描述
●参数一:指定线程池的线程数量(核心线程): corePoolSize ----------------------------------> 不能小于0

●参数二:指定线程池可支持的最大线程数: maximumPoolSize -----------> 最大数量 >= 核心线程数量

●参数三:指定临时线程的最大存活时间: keepAliveTime -----------------------------------------> 不能小于0

●参数四:指定存活时间的单位(秒、分、时、天): unit ----------------------------------------------> 时间单位

●参数五:指定任务队列: workQueue ------------------------------------------------------------------> 不能为null

●参数六:指定用哪个线程工厂创建线程: threadFactory ------------------------------------------> 不能为null

●参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler ---------------------------> 不能为null

抽象化理解:
●把线程池假设为一个餐厅,任务当作顾客,线程当作员工

●参数一:指定线程池的线程数量(核心线程): corePoolSize,就相当于餐厅里的正式员工,始终存在。

●参数二:指定线程池可支持的最大线程数: maximumPoolSize ,就相当于餐厅里的最多员工数,包括了正式员工和临时员工。

●参数三:指定临时线程的最大存活时间: keepAliveTime,是当每个顾客都有一名正式员工服务时,临时员工就空闲下来了,临时线程的最大存活时间就相当于临时员工最多可以空闲的多久后被开除。

●参数四:指定存活时间的单位(秒、分、时、天): unit ,相当于指定临时员工可以空闲的多久后被开除。

●参数五:指定任务队列: workQueue,就相当于餐厅外给等待的顾客坐的座位,座位数目是固定的。

●参数六:指定用哪个线程工厂创建线程: threadFactory,就相当于是餐厅里招服务员的HR

●参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler,假设餐厅里有10个正式员工,5个临时员工,来了15个顾客,此时每个员工都在忙,并且餐厅外等待的座位也坐满了,此时如果再来客人如何处理,就是handler

(4)线程池常见面试题
①临时线程什么时候创建?

●新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

②什么时候会开始拒绝任务?

●核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。

三、线程池处理Runnable任务

(1)ThreadPoolExecutor创建线程池对象示例

在这里插入图片描述

(2)ExecutorService的常用方法
方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行 Runnable 任务
Future submit(Callable task)执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
void shutdown()等任务执行完毕后关闭线程池
List shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
(3)新任务拒绝策略
策略详解
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionException异常。是默认的策略
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常 这是不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务 然后把当前任务加入队列中
ThreadPoolExecutor.CallerRunsPolicy由主线程负责调用任务的run()方法从而绕过线程池直接执行
(4)具体使用

在这里插入图片描述

①当任务数少于等于核心线程数时不会创建新的线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

②当任务数大于核心线程数并且小于等于核心线程数加上指定任务队列数目时,线程池认为现有的三个核心线程数忙的过来,也不会创建新的线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

③如果任务数大于核心线程数加上指定任务队列数目时,临时线程就会开始创建了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

④ 此时核心线程和临时线程均在忙,任务队列也满了,就触发了拒绝策略,不再创建!!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拒绝策略出发后,会丢弃任务并抛出RejectedExecutionException异常:

Exception in thread “main” java.util.concurrent.RejectedExecutionException: Task SummerDay36_threadpool.MyRunnable@34a245ab rejected from java.util.concurrent.ThreadPoolExecutor@7cc355be[Running, pool size = 5, active threads = 5, queued tasks = 5, completed tasks = 0]

(5)在使用ThreadPoolExecutor构造器时记不住参数有哪些怎么办呢?

在这里插入图片描述
不慌,根本不用强行记忆,按住Ctrl键,光标移到ThreadPoolExecutor上,单击后就会出现选择声明,选择最后一个有七个参数的声明点击,就可以看见这七个参数的具体使用啦~
在这里插入图片描述
在这里插入图片描述

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + "输出了:" + i);
        }
        try {
            System.out.println(Thread.currentThread().getName() + "本任务与线程绑定了,线程在忙");
            Thread.sleep(1000000);//让线程睡眠
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 目标:自定义一个线程池,并测试其特性
 */
public class ThreadDemo01 {
    public static void main(String[] args) {
        //1.创建线程池对象
        /**
         * public ThreadPoolExecutor(int corePoolSize,
         *                               int maximumPoolSize,
         *                               long keepAliveTime,
         *                               TimeUnit unit,
         *                               BlockingQueue<Runnable> workQueue,
         *                               ThreadFactory threadFactory,
         *                               RejectedExecutionHandler handler)
         */
        ExecutorService pool = new ThreadPoolExecutor(3,5,6,
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        //2.给任务线程池处理
        Runnable target = new MyRunnable();

        //① corePoolSize = 3 最大核心线程数是三,当任务数少于等于三个时不会创建新的线程
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        //② BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5)
        //当任务数 > 3 并且 <= (5+3) 时,线程池认为现有的三个核心线程数忙的过来,也不会创建新的线程
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);
        pool.execute(target);

        //③ 如果任务数 > 8 时,临时线程就会开始创建了
        pool.execute(target);
        pool.execute(target);

        //④ 此时三个核心线程和两个临时线程均在忙,任务满了,就触发了拒绝策略,不再创建!!
        pool.execute(target);
        pool.execute(target);

        //⑤ 关闭线程池(开发中一般是不会使用的)
        //pool.shutdownNow();//立即关闭,即使任务没有完成,丢失任务的!
        pool.shutdown();//会等待全部任务执行完毕之后再关闭

    }
}
(6)总结

线程池如何处理Runnable任务?
●使用ExecutorService的方法:
●void execute(Runnable target)

四、线程池处理Callable任务

(1)ExecutorService的常用方法
方法名称说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行 Runnable 任务
Future submit(Callable task)执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
void shutdown()等任务执行完毕后关闭线程池
List shutdownNow()立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
(2)线程池如何处理Runnable任务

●使用ExecutorService的方法:
●void execute(Runnable target)

(3)具体使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/**
 1、定义一个任务类 实现Callable接口  应该申明线程任务执行完毕后的结果的数据类型
 */
public class MyCallable implements Callable<String> {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    /**
     2、重写call方法(任务方法)
     */
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        return Thread.currentThread().getName()
                + "执行 1-" + n + "的和,结果是:" + sum;
    }
}
public class ThreadDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、创建线程池对象
        /**
         public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
         long keepAliveTime,
         TimeUnit unit,
         BlockingQueue<Runnable> workQueue,
         ThreadFactory threadFactory,
         RejectedExecutionHandler handler)
         */
        ExecutorService pool = new ThreadPoolExecutor(3, 5 ,
                6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5) , Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy() );

        //2.给任务线程池处理
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

//        String rs = f1.get();
//        System.out.println(rs);

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

五、Executors工具类实现线程池

(1)Executors得到线程池对象的常用方法

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

方法名称说明
public static ExecutorService newCachedThreadPool()线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。
public static ExecutorService newFixedThreadPool​(int nThreads)创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
public static ExecutorService newSingleThreadExecutor ()创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。
public static ScheduledExecutorService newScheduledThreadPool​(int corePoolSize)创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

●注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。

(2)具体使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(3)Executors使用可能存在的陷阱

●大型并发系统环境中使用Executors如果不注意可能会出现系统风险。

方法名称说明
public static ExecutorService newFixedThreadPool​(int nThreads)允许请求的任务队列长度是Integer.MAX_VALUE,可能出现OOM错误( java.lang.OutOfMemoryError )
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()创建的线程数量最大上限是Integer.MAX_VALUE,线程数可能会随着任务1:1增长,也可能出现OOM错误(java.lang.OutOfMemoryError)
public static ScheduledExecutorService newScheduledThreadPool​(int corePoolSize)
(4)总结
①Executors工具类底层是基于什么方式实现的线程池对象?

●线程池ExecutorService的实现类:ThreadPoolExecutor

②Executors是否适合做大型互联网场景的线程池方案?

●不合适。
●建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
如果你想成为一名JAVA暑假实习生,以下是一些你需要具备的能力: 1. 基本的编程技能:这是最基本的能力,需要熟练掌握JAVA编程语言及相关工具的使用,包括集成开发环境(IDE)、调试工具、版本控制工具等。 2. 熟悉面向对象编程思想:JAVA是一种面向对象编程语言,因此需要掌握面向对象编程的基本概念和原则,如封装、继承、多态等。 3. 数据结构和算法:数据结构和算法是计算机科学的核心知识,也是JAVA开发中不可或缺的一部分。需要掌握基本的数据结构,如数组、链表、栈、队列等,以及基本的算法,如排序、查找、递归等。 4. 数据库技术:JAVA常常与数据库一起使用,因此需要掌握基本的数据库知识,如SQL语言、关系型数据库设计等。 5. 熟悉WEB开发技术:JAVA的WEB开发框架非常丰富,如Spring、Struts、Hibernate等,需要掌握其中的一些框架,以及基本的HTML、CSS、JavaScript等前端技术。 6. 团队合作能力:在实习过程中,你需要与其他开发人员、项目经理等进行良好的合作,因此需要具备一定的团队合作能力。 7. 学习能力和解决问题能力:在实习过程中,你可能会遇到一些挑战和问题,需要具备良好的学习能力和解决问题的能力,能够快速学习新技术和解决问题。 以上是JAVA暑假实习生需要具备的一些能力,当然还有其他的技能和要求,不同公司和岗位的具体要求也可能会有所不同,希望能对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呦呦呦欸哟哟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值