前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你了解线程池吗?

你了解线程池吗?

作者头像
石的三次方
发布2021-01-05 22:49:07
4320
发布2021-01-05 22:49:07
举报
文章被收录于专栏:石的三次方石的三次方

线程池

1. 简介

池的概念,在数据库中存在连接池,在字符串中存在常量池等等池的概念都是一个道理。

为了「防止在使用的时候创建连接和销毁的时间损耗」,于是选择在服务开启的时候就创建一部分连接供后续使用,使用完以后放入池中,形成复用,而出现的池的概念.

线程池也是如此,在服务启动的时候通过spring或者静态方法等初始化一个池子,之后的所有请求任务都直接添加到这个线程池中,线程池中的线程直接执行添加到线程池中的任务。

线程池存在一个核心线程数,就是在初始的时候,线程拥有的线程数量,当核心线程数不足以处理传入的任务的时候,就会将这个任务添加到阻塞队列中。

当阻塞队列饱和以后,线程池再次创建线程,直到最大线程数。

如果此时已经达到了最大线程数,还是无法处理任务,就会按照指定的拒绝策略拒绝任务。

当任务量不大的时候,线程会有空闲,当线程的空闲时间超过我们指定的最大空闲时间以后,就会被线程池销毁。

2. 线程池的基本参数

根据上面的讲解,我们很容易知道一个线程池需要什么参数。

首先,一个初始化线程数,即核心线程数coreSize

  • 一个阻塞队列,BlockingQueue
  • 一个最大线程数,maxSize
  • 一个拒绝策略,RejectedExecutionHandler
  • 一个最大空闲时间和时间单位,keepAliveTimetimeUnit
  • 还有一个线程工程帮助我们创建一个线程threadFactory

这就是一个线程池的七大参数,下面我们根据这个参数构建一个线程池

3. 自定义线程池

「本次线程池仅仅模拟了四个参数的使用,核心线程,最大线程,队列最大长度」

「任务类」

代码语言:javascript
复制
public class MyTask implements Runnable{

    int id  ;

    public MyTask(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+"\t即将执行任务"+id);
        try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
        System.out.println(name+"\t完成了任务");
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}

「线程类」

代码语言:javascript
复制
/**
 * 自定义一个线程
 */
public class MyThread extends Thread{
    private String name ;
    private List<Runnable> tasks ;

    public MyThread(String name, List<Runnable> tasks) {
        this.name = name;
        this.tasks = tasks;
    }

    @Override
    public void run() {
        while(tasks.size()>0){
            Runnable remove = tasks.remove(0);
            remove.run();
        }
    }
}

「线程池类」

代码语言:javascript
复制
/**
 * 自定义的线程池
 */
public class MyPool {

    private int currentPoolSize ;
    private int corePoolSize ;
    private int maxPoolSize ;
    private int workSize ;
    private List<Runnable> tasks = new CopyOnWriteArrayList<>();

    public MyPool(int corePoolSize, int maxPoolSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxPoolSize = maxPoolSize;
        this.workSize = workSize;
    }

    public void sumbit(Runnable runnable){
        //如果当前线程数小于核心线程数
        if(tasks.size()>=workSize){
            System.out.println("任务被丢弃");
        }else{
            tasks.add(runnable);
            this.execTask(runnable);
        }
    }

    public void execTask(Runnable runnable){
        if(currentPoolSize <= corePoolSize){
            new MyThread("线程"+currentPoolSize,tasks).start();
            currentPoolSize++;
        }else if(currentPoolSize < maxPoolSize){
            new MyThread("非核心线程"+currentPoolSize,tasks).start();
            currentPoolSize++;
        }else{
            System.out.println(runnable+"任务被缓存");
        }
    }

}

「执行器」

代码语言:javascript
复制
public class MyThreadPollTest {
    public static void main(String[] args) {
        MyPool myPool = new MyPool(2,4,20);
        for (int i = 0; i < 30; i++) {
            MyTask myTask = new MyTask(i);
            myPool.sumbit(myTask);
        }
    }
}

「输出器」

代码语言:javascript
复制
yTask{id=4}任务被缓存
MyTask{id=5}任务被缓存
MyTask{id=6}任务被缓存
MyTask{id=7}任务被缓存
MyTask{id=8}任务被缓存
MyTask{id=9}任务被缓存
MyTask{id=10}任务被缓存
MyTask{id=11}任务被缓存
MyTask{id=12}任务被缓存
MyTask{id=13}任务被缓存
MyTask{id=14}任务被缓存
MyTask{id=15}任务被缓存
MyTask{id=16}任务被缓存
MyTask{id=17}任务被缓存
MyTask{id=18}任务被缓存
MyTask{id=19}任务被缓存
Thread-3 即将执行任务2
Thread-2 即将执行任务1
Thread-1 即将执行任务0
MyTask{id=20}任务被缓存
MyTask{id=21}任务被缓存
MyTask{id=22}任务被缓存
Thread-0 即将执行任务3
MyTask{id=23}任务被缓存
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
任务被丢弃
Thread-3 完成了任务
Thread-3 即将执行任务4
Thread-2 完成了任务
Thread-2 即将执行任务5
Thread-1 完成了任务
Thread-1 即将执行任务6
Thread-0 完成了任务
Thread-0 即将执行任务7
Thread-1 完成了任务
Thread-3 完成了任务
Thread-1 即将执行任务8
Thread-3 即将执行任务9
Thread-0 完成了任务
Thread-2 完成了任务
Thread-2 即将执行任务10
Thread-0 即将执行任务11
Thread-3 完成了任务
Thread-1 完成了任务
Thread-1 即将执行任务12
Thread-3 即将执行任务13
Thread-0 完成了任务
Thread-0 即将执行任务14
Thread-2 完成了任务
Thread-2 即将执行任务15
Thread-1 完成了任务
Thread-1 即将执行任务16
Thread-3 完成了任务
Thread-3 即将执行任务17
Thread-2 完成了任务
Thread-0 完成了任务
Thread-0 即将执行任务19
Thread-2 即将执行任务18
Thread-3 完成了任务
Thread-1 完成了任务
Thread-1 即将执行任务21
Thread-3 即将执行任务20
Thread-0 完成了任务
Thread-0 即将执行任务22
Thread-2 完成了任务
Thread-2 即将执行任务23
Thread-1 完成了任务
Thread-3 完成了任务
Thread-0 完成了任务
Thread-2 完成了任务

4. Java线程池

「线程体系图」

Java中提供了一个Executors类获取一些特定的线程

4.1 没有核心线程的线程池
代码语言:javascript
复制
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                60L, TimeUnit.SECONDS,
                                new SynchronousQueue<Runnable>());
    }

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                              new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
4.2 单例线程池

这个线程池仅仅创建一个核心线程

代码语言:javascript
复制
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
             0L, TimeUnit.MILLISECONDS,
              new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                  new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
4.3 定时线程池

可以执行定时任务的线程池

代码语言:javascript
复制
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
  • scheduleAtFixedRate间隔时间中包含了执行任务的时间
  • scheduleWithFixedDelay间隔时间中不包含执行任务的时间

4.4 ThreadPoolExecutor

这是Java提供的一个线程池,在开发中我们经常直接使用这种自定义的线程池

「构造方法:」

代码语言:javascript
复制
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

下面我们着重了解一下「阻塞队列」「拒绝策略」

4.4.1 阻塞队列

BlockingQueue是一个接口,看一眼他的继承体系图

  • ArrayBlockingQueue由数组构成的一个有界的阻塞队列
  • LinkedBlockingQueue由链表构成的一个有界的阻塞队列
  • SynchronousQueue不存储元素的队列,仅仅存放一个队列
  • LinkedBlockingQueue双端有界队列。
  • PriorityBlockingQueue支持优先级排序的有界队列
  • DelayQueue使用优先级队列实现的延迟无界队列
  • LinkedTransferQueue由链表结构组成的无界阻塞队列

「三种执行方式」

  • 抛出异常。add()和remove()
代码语言:javascript
复制
java.lang.IllegalStateException
  • 返回布尔或者null。offerpoll
代码语言:javascript
复制
添加返回false;移除返回null
  • 线程阻塞。puttake
  • 查看之后的元素elementpeek
4.4.2 拒绝策略

线程池默认的拒绝策略是直接抛出异常

线程池存在四种拒绝策略

  • AbortPolicy直接抛出异常,默认拒绝策略
  • CallerRunsPolicy将任务返回给调用者执行,即将任务返回给线程池的线程执行
  • DiscardPolicy直接拒绝
  • DiscardOldestPolicy尝试将阻塞队列中等待时间最长的出队,将最新的加入
4.4.3 线程池参数设置

8020原则,系统80%的时间内一秒都会产生100个任务,一个任务执行时间为0.1秒。一秒最多产生任务数量为1000

  • 核心线程数。100*0.1=10
  • 任务队列长度。100*2=200
  • 最大线程数。(最大任务数-任务队列长度)*单个任务执行时间 (1000-200)*0.1=800

5. ExecutorService

上面线程池继承体系中,有一个ExecutorService。这是Java中内置的线程池接口

代码语言:javascript
复制
void shutdown()   启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 
List<Runnable> shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 
<T> Future<T> submit(Callable<T> task)  执行带返回值的任务,返回一个Future对象。 
Future<?> submit(Runnable task)  执行 Runnable 任务,并返回一个表示该任务的 Future。 
<T> Future<T> submit(Runnable task, T result)  执行 Runnable 任务,并返回一个表示该任务的 Future。 
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 石的三次方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线程池
    • 1. 简介
      • 2. 线程池的基本参数
        • 3. 自定义线程池
          • 4. Java线程池
            • 4.1 没有核心线程的线程池
            • 4.2 单例线程池
            • 4.3 定时线程池
          • 5. ExecutorService
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档