并发编程--线程池

在上一节中,已经介绍了线程之间是如何进行通讯的。今天让我们一起来学习线程池相关的知识。

为什么要使用线程池?

线程池是jdk1.5 才出现的;目的是为了更好的创建线程。其实先在真实的项目中,我们并不是直接通过继承Thread类或者实现Runnable来创建线程的,更多的是通过线程池这种方法来创建线程;使用线程池创建的线程的好处:

  1. 降低资源消耗:通过重复利用已创建的线程避免多次创建或销毁线程带来的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用。
线程池作用

线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。

线程池四种创建方式
newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 public static void main(String[] args) {
        //创建可缓存线程池对象
        ExecutorService executorService = Executors.newCachedThreadPool();
        for ( int i = 0; i < 30; i++) {
             int a = i;
            executorService.execute(() -> System.out.println(Thread.currentThread().getName() +"打印--> "+a));
        }
    }

执行结果:
在这里插入图片描述
我们的需要时从0打印到29,发现并不是直接创建30个线程。
总结: 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  public static void main(String[] args) {
        //创建一个定长的线程池,超出的线程会在队列中等待
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for ( int i = 0; i < 30; i++) {
             int a = i;
            executorService.execute(() -> System.out.println(Thread.currentThread().getName() +"打印--> "+a));
        }
    }

执行结果:

在这里插入图片描述
从执行结果可以看出,线程池只创建了5条线程来打印。

newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

  public static void main(String[] args) {
        //创建一个定长的线程池,支持定时及周期性任务执行
        ExecutorService executorService = Executors.newScheduledThreadPool(5);
        for ( int i = 0; i < 30; i++) {
             int a = i;
             //执行任务调度
           ((ScheduledExecutorService) executorService).schedule(() -> System.out.println(Thread.currentThread().getName()+"打印----> "+a),3, TimeUnit.SECONDS);
        }
        //关闭线程池
        executorService.shutdown();
    }

表示延迟3秒执行。

newSingleThreadExecutor

建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 public static void main(String[] args) {
        //创建一个单一线程的线程池,
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for ( int i = 0; i < 30; i++) {
             int a = i;
             //执行任务调度
           executorService.execute( () -> System.out.println(Thread.currentThread().getName()+"打印----> "+a));
        }
        //关闭线程池
        executorService.shutdown();
    }

注意: 结果依次输出,相当于顺序执行各个任务。

线程池原理剖析

提交一个任务到线程池中,线程池的处理流程如下:
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
在这里插入图片描述

合理配置线程池
  • CPU密集型
    CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
    CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程,该任务都不可能得到加速,因为CPU总的运算能力就那些。

  • IO密集型
    IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以在IO密集型任务中使用多线程可以大大的加速程序运行,即时在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。
    总结:
    CPU密集型时,任务可以少配置线程数,大概和机器的cpu核数相当,这样可以使得每个线程都在执行任务
    IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值