JUC 常用知识 2(线程池)

线程池

池化技术

由于程序的运行,本质是在占用系统的资源!而为了优化资源的使用,就诞生了池化技术,比如有线程池、常量池、JDBC的连接池…

线程池的好处:

  • 1、降低资源的消耗
  • 2、提高响应的速度
  • 3、方便管理
  • 4、线程复用,可以控制最大并发数,管理线程

线程池:三大方法,七大参数,四种拒绝策略

三大方法

一、创建单个线程池 => Executors.newSingleThreadExecutor();

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();// 单个线程
// 启动线程池
try {
   for (int i = 0; i < 10; i++) {
       threadExecutor.execute(()->{
           System.out.println(Thread.currentThread().getName()+" ok");
       });
   }
} catch (Exception e) {
   e.printStackTrace();
} finally {
   // 关闭线程池
   threadExecutor.shutdown();
}

结果:所有线程都在1个线程池里
在这里插入图片描述

二、创建一个固定大小的线程池 => Executors.newFixedThreadPool(5);

ExecutorService threadPool = Executors.newFixedThreadPool(5);// 创建一个固定大小的线程池
// 启动线程池
try {
   for (int i = 0; i < 10; i++) {
       threadPool.execute(()->{
           System.out.println(Thread.currentThread().getName()+" ok");
       });
   }
} catch (Exception e) {
   e.printStackTrace();
} finally {
   // 关闭线程池
   threadPool.shutdown();
}

结果:固定大小,最多5个线程池
在这里插入图片描述

三、创建可伸缩的线程池 => Executors.newCachedThreadPool();

ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的线程池
// 启动线程池
try {
  for (int i = 0; i < 10; i++) {
      threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+" ok");
      });
  }
} catch (Exception e) {
  e.printStackTrace();
} finally {
  // 关闭线程池
  threadPool.shutdown();
}

结果:可以根据有多少个线程来建多少个线程池(最多能多少个也不确定,反正遇强则强)!
在这里插入图片描述

创建线程池的正确打开方式

然而,阿里巴巴开发手册上却不允许使用 Executors 类来创建线程池,要使用原生的 ThreadPollExecutor 来创建线程池:
在这里插入图片描述
所以正确创建建方式为:

为方便理解,就以一个银行取钱的实例来模仿创建线程池

// 自定义线程池(在工作中只会使用这种方式)
ExecutorService threadPool = new ThreadPoolExecutor(
        2, // 银行默认开启2个窗口服务
        5, // 银行总共有五个窗口
        3, // 休息区满后又来人,银行就开启另外三个窗口,如果这三个窗口在三秒内没人来,就会再次关闭
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<>(3), // 休息区
        Executors.defaultThreadFactory(), // 创建线程
        new ThreadPoolExecutor.AbortPolicy() // 银行所有东西都满了,却还有人进来,就不处理这个人了,并抛出异常
);
// 启动线程池
try {
    for (int i = 1; i <= 9; i++) {
        threadPool.execute(()->{
            System.out.println(Thread.currentThread().getName()+" ok");
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    // 关闭线程池
    threadPool.shutdown();
}

结果:当i超过最大承载量(窗口数+休息区数)时,就会报错。
在这里插入图片描述

七大参数

七大参数就创建线程池所需的7个参数:

int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

拒绝策略
在这里插入图片描述
拒绝策略就是创建线程池的最后一个参数,一共如上图所示,有4种策略:

ThreadPoolExecutor.AbortPolicy():队列就会报错;

ThreadPoolExecutor.CallerRunsPolicy():哪来的就哪去,就是超出后来的由 main 线程去处理;

ThreadPoolExecutor.DiscardPolicy():队列满了就抛弃超出的任务,但不会抛出异常;

ThreadPoolExecutor.DiscardOldestPolicy():队列满后,尝试去和最早的线程竞争,也不会抛出异常;


问题:线程池的最大容量该如何定义(面试会问)?

两种解决方案(这是用来调优的了):

一、CPU 密集型

即有几核 CPU,就会有几个线程可以同时执行。所以你是几核 CPU,就定义为几,可以保证 CPU 效率最高。

通过代码获取电脑 CPU 的核数:

Runtime.getRuntime().availableProcessors();
二、IO 密集型

就是判断你的程序中,非常耗 IO 的线程有几个,那么定义的数大于几就可以了(一般可以设置为大于几的两倍)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值