zoukankan      html  css  js  c++  java
  • 多线程之线程池 · 下

    前言

    线程池中这块的内容确实要比我预期的多,当然也可能是我讲的太细了,所以比较费字,但是这样也好,不仅让各位小伙伴能更清楚相关逻辑和原理,而且对我而言,让我可以做到知其然知其所以然,从这个层面上来讲,我觉得一切都值得,甚至还有点意外的收获。

    今天是线程池相关知识点收官之作,今天的内容完结后,基本上线程池这块的内容就可以告一段落了,今天主要从以下几方面开展:

    • 线程池的其他参数
      • 线程工厂
      • 拒绝策略处理器

    好了,话不多说,让我们直接开始吧。

    线程池

    线程工厂

    先看第一个参数——线程工厂,这个参数的作用是创建线程。在最开始的时候,我们创建线程池的时候并没有指定这个参数,但是构造器内部会自动为我们引入默认的线程工厂:

    下面我们就来看下默认的线程工厂是如何实现的:

    在线程工厂中,主要有两部分的操作,第一部分就是设定创建线程时的信息,包括线程组、线程名称、堆栈大小;第二部分主要是设置线程的优先级,如果线程是守护线程的话,会把它修改为非守护线程(看到这里,我发现关于线程组、线程得好好补补课了,后面要跟进下)。

    我们一般自定义线程工厂主要是为了修改线程名称,这样方便我们在排查问题的时候找到我们自己定义的线程池,我们要自顶一下线程工厂只需要冲洗ThreadFactorynewThread方法即可:

    static class MyThreadFactory implements ThreadFactory {
            private static final AtomicInteger poolNumber = new AtomicInteger(1);
            private final ThreadGroup group;
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            private final String namePrefix;
    
            MyThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                        Thread.currentThread().getThreadGroup();
                namePrefix = "syske-" +
                        poolNumber.getAndIncrement() +
                        "-thread-";
            }
    
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(group, r,
                        namePrefix + threadNumber.getAndIncrement(),
                        0);
                if (t.isDaemon())
                    t.setDaemon(false);
                if (t.getPriority() != Thread.NORM_PRIORITY)
                    t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }
    

    这里我就直接把默认线程工厂的实现copy过来,然后只改了名字,然后在构造线程池的时候传入即可:

            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new MyThreadFactory());
    

    然后运行下,我们就会发现线程名称已经变成我们修改之后的了:

    当然如果只是单纯想修改线程池中线程名称,这样就太奢侈了,我们可以通过guava提供的ThreadFactoryBuilder直接修改(就是我们之前分享的guava):

    ThreadFactory build = new ThreadFactoryBuilder().setNameFormat("syske-task-%d").build();
    

    当然,guava还可以设定其他属性,甚至可以指定线程工厂,UncaughtExceptionHandler表示未捕获到的异常处理器:

    拒绝策略(饱和策略)

    我们在之前的内容中说过,如果任务数超过corePoolSize + maximumPoolSize + workQueue.size(),线程池就会报拒绝连接的错误,这个错误就是这里抛出了的,所以接下来我们要分享的就是线程池的最后一个参数——RejectedExecutionHandler

    和线程工厂一样,在我们不传拒绝策略处理器的时候,其实构造方法内部为我们制定了默认的处理器:

    默认拒绝策略处理器的实现也很简单:

    也可以说是简单粗暴,直接就抛出了RejectedExecutionException异常。

    当然我们也可以定义自己的拒绝策略处理器:

     static class MyRejectedExecutionHandler implements RejectedExecutionHandler {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("线程池拒绝连接,资源已耗尽:r = " + r + ", executor = " + executor);
                throw new RejectedExecutionException();
            }
        }
    

    然后也是在构造线程池的时候传入:

    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new MyThreadFactory(), new MyRejectedExecutionHandler());
    

    我们把工作对了调小,把循环次数调大,把休眠时间调长,然后运行就会触发饱和策略,进入我们的的rejectedExecution方法:

    根据我的测试,我发现如果在rejectedExecution方法中直接抛出RejectedExecutionException,会导致主线程进入阻塞状态,这样整个系统就卡死了

    但如果不抛出RejectedExecutionException异常的话,则不会导致阻塞:

    所以考虑到实际应用情况,我觉得我们还是有必要重写rejectedExecution方法的,而且我们在方法内部还可以做一些业务操作,比如线程池扩容,或者睡眠等待:

    或者可以做一些业务告警等操作。

    总结

    关于线程池的创建、参数以及参数的作用,经过我们这两天的详细分析和演示,我想大家一定也对线程池有了新的认知,应该说在以后的工作和学习中,不论是线程池的使用还是解决线程池相关的问题,都可以站得更高,看的更远。

    当然,最重要的是,这几天分享的内容都比较实用。明天我们会对线程池这块做一个小结,然后小结之外我们会有一些遗漏知识点的补充。好了,今天就先到这里吧!

  • 相关阅读:
    python多继承下的查找顺序-MRO原则演变与C3算法
    linux后台启动项目命令
    Django框架—ORM操作笔记
    Git使用方法记录
    django框架—终端命令
    数据中台实战之元数据
    MYSQL之高性能的mysql(七)--MySQL高级特性
    MYSQL之高性能的mysql(六)--查询性能优化
    MYSQL之高性能的mysql(五)--索引
    MYSQL之高性能的mysql(四)--Schema与数据类型优化
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/15024204.html
Copyright © 2011-2022 走看看