浅谈线程池

先解释一下两个概念,核心线程数和最大线程数的区别,比如电商网站平常我们可以用50个线程来处理用户的所有请求,但是当双十一的时候请求明显增多这是我们需要用500个线程来解决用户的请求,50就是核心线程数,500就是最大线程数
先上线程池流程图吧
浅谈线程池
(图片来源博客园@徐志毅侵删)

第一种Executors.newCacheThreadPool()

这种方式没有入参,核心线程数为0个,最大线程数为Integer.maxValue空闲线程60m关闭,使用简单无需参数

第二种Executors.newFixedThreaPool(设置核心线程数及最大线程数)

这种方式可以根据我们服务器的配置来动态设置最大线程数提高硬件利用率

第三种Executors.newSingleThreadPool()

顾名思义就是只有一个线程来处理任务

第四种Executors.newScheduledThreadPool(设置核心线程数)

这种方式可以做定时功能,返回值为ScheduledExecutorService,设返回值为m可以使用m.schedule(Runnable,定时时长,时长单位)方法来定时执行Runnable中的任务,也可以调用m.scheduleAtFixedRate(Runnable,定时时长,执行频率,时长单位)方法来延时执行Runnable中的任务并每过一段时间(执行频率)重复执行。这个也有Single的类这里就不说了

第五种ForkJoin.commonPool()获得一个ForkJoinPool类

这种线程池体现的是一种分治思想需要任务类继承RecursiveAction这个抽象类把一个任务分解成多个子任务来执行


常见的线程池我能想到的就上述五种,而对于上述五种都不建议采用,原因是入参太少了,不利于我们控制线程池的参数,灵活性差,翻阅源码他们最终都是new的ThreadPoolExecutor类,下面我们分析一下ThreadPoolExecutor类

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

上面是创建线程池的方法以及参数

**corePoolSize代表线程池的核心线程数
maximumPoolSize代表线程池的最大线程数
keepAliveTime表示空闲线程多久关闭
unit表示关闭时间的单位
workQueue表示当核心线程满时的阻塞队列
ThreadFactory 表示线程工厂一般使用默认的
handler表示丢弃策略**
以比较复杂一点的延迟线程池为例

ExecutorService m = new ThreadPoolExecutor(
1      //代表线程池的核心线程数
,10    //代表线程池的最大线程数
,60L    //表示空闲线程多久关闭
,TimeUnit.SECONDS //表示关闭时间的单位
,new DelayQueue()//表示当核心线程满时的阻塞队列
,new ThreadPoolExecutor.DiscardPolicy()
//表示丢弃策略
);

前四个参数没有什么好说的从阻塞队列开始,阻塞队列常用的有四种:
**LinkedBlockingQueue 基于链表的生产消费使用两把锁可以同时进行
ArrayBlockingQueue 基于数组所以需要提前制定大小,且生产消费使用同一把锁不可以同时进行
DelayedWorkQueue 可以对任务进行定时,生产消费使用同一把锁。
SynchornoursQueue 与上述阻塞队列不同,同步阻塞队列不存储任务,只负责将生产线程的数据搬运给消费线程,吞吐量高。**
对于定时的线程池我们需要我们添加的任务类必须实现了Delayed接口并且是一个线程类,上代码

private long timeOut;
    private long delayTime;
    
    //初始化时需要设置到期时间以纳秒为单位
    public DelayedTask(long timeOut){
        this.timeOut = timeOut;
        this.delayTime = System.nanoTime()+TimeUnit.NANOSECONDS.convert(timeOut,TimeUnit.SECONDS);
    }

    //返回还有多长时间任务需要被执行
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.delayTime-System.nanoTime(),TimeUnit.NANOSECONDS);
    }

    
    @Override
    public int compareTo(Delayed o) {
        if(o == this){
            return 0;
        }
        if(o.getDelay(TimeUnit.NANOSECONDS)<this.getDelay(TimeUnit.NANOSECONDS)){
            return 1;
        }
        return -1;
    }  

上述代码为延时阻塞队列中任务的要求
可以像ScheduledExecutorService一样直接添加任务就行到时自动会执行
下一个参数丢弃策略

ThreadPoolExecutor类中定义了四种丢弃策略,当核心线程、阻塞队列已满且当前线程数已经达到最大值时线程池就会执行丢弃策略。

AbortPolicy:丢弃该任务并抛出异常
DiscardPolicy:丢弃任务但不抛出异常
DiscardOldestPolicy:丢弃最旧的未处理的任务然后重试
CallerRunsPolicy:使用提交任务的线程来执行任务
关于线程池的阻塞队列用什么还是需要根据业务需求来定,本文章仅为参考

上一篇:《网络安全法》施行展望:谁会是第一个吃螃蟹的人?


下一篇:前沿观察:云数据库安全发展趋势