JUC编程——(四)线程池(七大参数,四种拒绝策略,自定义一个线程池,合理设置最大线程数,线程池队列的选择)

线程池

1、三大方法
public class Demo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();//1.创建单个线程的线程池
        ExecutorService executorService1 = Executors.newFixedThreadPool(5);//2.创建固定线程数量的线程
        ExecutorService executorService2 = Executors.newCachedThreadPool();//3.根据任务的多少创建线程数量
        try {
            for (int i = 0; i < 100; i++) {
                executorService2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完之后关闭线程池
            executorService2.shutdown();
        }
    }
}
2、七大参数

我们通过阅读源码可以发现,上面创建线程池的方法底层都是调用ThreadPoolExecutor这个方法进行的,它又七个参数。

public ThreadPoolExecutor(int corePoolSize,//核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//超时没有人调用会被释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂:创建线程
                              RejectedExecutionHandler handler) //拒绝策略
                              {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

以银行办业务为例解释一下这七大参数。
图1
在这里插入图片描述
图2
在这里插入图片描述

3、四种拒绝策略

在这里插入图片描述

1. new ThreadPoolExecutor.AbortPolicy(),不处理直接抛出异常。java.util.concurrent.RejectedExecutionException。
2. new ThreadPoolExecutor.CallerRunsPolicy(),哪来的去哪去,交由主线程处理。
3. new ThreadPoolExecutor.DiscardPolicy(),直接丢掉任务,不会抛出异常。
4. new ThreadPoolExecutor.DiscardOldestPolicy(),去尝试和线程开启最早的竞争cpu,也不会抛出异常
4、自定义线程池
package com.JUC编程.线程池;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyExecutors {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),//默认的线程创建工厂
                new ThreadPoolExecutor.AbortPolicy());//不处理,直接抛出异常
        try {
            for (int i = 1; i <=9; i++) {
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"被使用");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
    }
}

测试

2个人时:开启了1个线程
5个人时:还是开启了2个线程
6个人时:开启了3个线程
7个人时:开启了4个线程
8个人时:开启了5个线程
9个人时:根据不同的拒绝策略做出反应
5、如何合理的设置最大线程数?

分析下线程池处理的程序是CPU密集型,还是IO密集型

(1)CPU密集型

CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。
在多重程序系统中,大部分时间用来做计算、逻辑等CPU动作称之为CPU bound,例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部分时间用在三角函数和开根号的计算,便是属于CPU bound的程序。
CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。

(2)IO密集型

IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU等等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。
I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。

(3)获取电脑的cpu核数
 System.out.println(Runtime.getRuntime().availableProcessors());
(4)设置
  • CPU密集型:CPU的核数是多少,最大线程数设为多少。
  • IO密集型:判断程序中是否耗IO的线程的数量,最大线程数就设置为这个的2倍。
6、线程池队列的选择

当线程数组超过核心线程数时用于保存任务的队列,主要有三种类型的BlockingQueue可供续选择:无界队列,有界队列,和同步移交。

(1)无界队列

队列的大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时尤其要当心,当任务耗时较长时可能会导致大量新任务在队列中堆积导致OOM,newFixedThreadPool采用的就是这个。
当然对于这种队列,最大线程数的值也就无效了,**当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。**例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

(2)有界队列

当使用有限的最大线程数时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

(3)同步队列

如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值