前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文简单理解Java线程池的问题

一文简单理解Java线程池的问题

作者头像
暴躁的程序猿
发布2022-03-24 15:36:05
1390
发布2022-03-24 15:36:05
举报
文章被收录于专栏:阿飞的学习记录

线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核充分利用,还能防止过分调度。 线程池架构

好处:

降低资源的消耗,提高响应速度, 方便管理

线程复用、可以控制最大并发数、管理线程

特征:

线程池中的线程有一定的量 可以控制并发量 线程可以重复使用 在关闭之前都可以一直使用 超过一定量的线程提交执行时需要在队列中等待

架构

java通过Executor框架实现的 该框架中用到了Executor,Executors,ExecutorService

ThreadPoolExecutor这几个类

在这里插入图片描述
在这里插入图片描述

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

三大方法:

代码语言:javascript
复制
//单个线程 只有一个线程
Executors.newSingleThreadExecutor()
 //创建固定大小的线程池   
Executors.newFixedThreadPool(6)
//创建一个可以伸缩的线程池
Executors.newCachedThreadPool()

newFixedThreadPool使用示例

创建一个有6个线程的线程池 10条线程进行请求

线程当执行了execute()方法时线程才会创建

代码语言:javascript
复制
public class Demo1 {
    public static void main(String[] args) {
        //单个线程
//        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建固定大小的线程池
        ExecutorService pool = Executors.newFixedThreadPool(6);
        //创建一个可以伸缩的线程池
//        ExecutorService pool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            pool.shutdown();
        }
    }
}

最多只有六个线程 因为我们指定了6个

在这里插入图片描述
在这里插入图片描述

newSingleThreadExecutor使用示例

代码语言:javascript
复制
public class Demo1 {
    public static void main(String[] args) {
        //单个线程
        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建固定大小的线程池
//        ExecutorService pool = Executors.newFixedThreadPool(6);
        //创建一个可以伸缩的线程池
//        ExecutorService pool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            pool.shutdown();
        }
    }
}

只有一个线程

在这里插入图片描述
在这里插入图片描述

newCachedThreadPool使用示例

代码语言:javascript
复制
public class Demo1 {
    public static void main(String[] args) {
        //单个线程
//        ExecutorService pool = Executors.newSingleThreadExecutor();
        //创建固定大小的线程池
//        ExecutorService pool = Executors.newFixedThreadPool(6);
        //创建一个可以伸缩的线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                pool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完要关闭
            pool.shutdown();
        }
    }
}

可以看到 产生了七个线程

在这里插入图片描述
在这里插入图片描述

但是根据阿里巴巴开发手册规定 不应该使用Executors创建线程 会产生问题

在这里插入图片描述
在这里插入图片描述

所以我们应该使用ThreadPoolExecutor创建线程池 而且我们通过源码可以发现 以上的方式还是调用的ThreadPoolExecutor创建

源码

代码语言:javascript
复制
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

我们可以看到ThreadPoolExecutor源码中定义了七个参数

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

下面我们详细分析一下这七个参数

七大参数

代码语言:javascript
复制
 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;
    }

int corePoolSize, 核心线程池大小 相当于银行的办理业务窗口 肯定有两个窗口是一直开放的

int maximumPoolSize, 最大核心线程池大小 银行的窗口 一共有10个窗口 有两个是一直开放的 其他的在银行忙的时候会开放

long keepAliveTime, 空闲存活时间 其他的窗口开放了 办理业务也完成了 其他开放的窗口没有人办理业务了 等待多长时间没人来办理就会关闭 只留下一直开放的窗口 超时没有人调用就会释放 TimeUnit unit, 时间的单位

BlockingQueue workQueue, 阻塞队列 相当于办理业务时等待叫号的候客厅

ThreadFactory threadFactory, 线程工厂 创建线程的 不用动

RejectedExecutionHandler handler 拒绝策略 当银行的所有窗口都满了 候客厅也满了 就会拒绝为新来的客户办理业务 拒绝策略有四种 稍后会详细分析

使用示例

代码语言:javascript
复制
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) {
        //自定义线程池
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2,
                5,
                //空闲存活时间  三秒钟没有人来这个窗口办理业务  额外的窗口就会关闭
                3,
                TimeUnit.SECONDS,
                //相当于候客区  候客区满了  就会同时开启五条线程
                new ArrayBlockingQueue<>(3),
                //默认的线程工厂不用动
                Executors.defaultThreadFactory(),
                //拒绝策略  银行满了还有人进来,就不处理这个人的业务 抛出异常
                new ThreadPoolExecutor.DiscardOldestPolicy()
                );

        try{
            for (int i = 0; i < 9 ; i++) {
                //最大并发数  最大线程池大小+队列大小
                poolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
                poolExecutor.shutdown();
        }
    }
}
在这里插入图片描述
在这里插入图片描述

小细节:

线程执行了execute()方法时才创建的

加入指定了一个核心为3个线程最大线程为5 阻塞队列为2的线程池

那么前三个线程处理业务时 来的第四个跟第五个 线程会进入阻塞队列等待

来的第六个线程会 开启第四个窗口来优先处理第六个线程

四大拒绝策略

在这里插入图片描述
在这里插入图片描述

new ThreadPoolExecutor.AbortPolicy() 丢弃任务 并抛出RejectedExecutionException异常 【 默认 】

最大处理线程数满了就不处理新来的线程并抛出异常

代码语言:javascript
复制
public class ThreadPoolExecutorDemo2 {
    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 = 0; i < 9; i++) {
                //获取线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //归还线程
            threadPoolExecutor.shutdown();
        }



    }
}
在这里插入图片描述
在这里插入图片描述

new ThreadPoolExecutor.CallerRunsPolicy() 由调用线程处理该任务 会用main线程

代码语言:javascript
复制
public class ThreadPoolExecutorDemo2 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                 //最大线程数
                5,
                 //存活时间
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                //线程工厂
                Executors.defaultThreadFactory(),
                //拒绝策略
                new ThreadPoolExecutor.CallerRunsPolicy()
                );
        try{
            for (int i = 0; i < 9; i++) {
                //获取线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //归还线程
            threadPoolExecutor.shutdown();
        }



    }
}
在这里插入图片描述
在这里插入图片描述

new ThreadPoolExecutor.DiscardPolicy() 队列满了就会丢掉任务不会抛出异常 new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃队列最前面的任务,然后重新尝试执行任务 不会抛出异常 如果没竞争到也会丢掉任务

代码语言:javascript
复制
public class ThreadPoolExecutorDemo2 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                 //最大线程数
                5,
                 //存活时间
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                //线程工厂
                Executors.defaultThreadFactory(),
                //拒绝策略
                new ThreadPoolExecutor.DiscardPolicy()
                );
        try{
            for (int i = 0; i < 9; i++) {
                //获取线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //归还线程
            threadPoolExecutor.shutdown();
        }



    }
}

多出的线程任务被丢弃了

在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/07/20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档