java并发之线程池


1:ThreadPoolExcutor

(1):ThreadPoolExcutor构造函数

   public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

(2):构造函数的参数

  • corePoolSize核心线程数目(最多保留的线程数)
  • maximumPoolSize最大线程数目
  • keepAliveTime生存时间-针对救急线程
  • unit时间单位-针对救急线程
  • workQueue阻塞队列
  • threadFactory线程工厂-可以为线程创建时起个好名字
  • handler拒绝策略

(3):图示 核心线程数 最大线程数 阻塞队列

  • 核心线程数就是我们银行一直正常开的窗口
  • 阻塞队列就是银行常用窗口人数满了,然后就在后候客区等待的顾客
  • 救急线程就是阻塞队列满了 不得不再开几个营业窗口
  • 最大线程数目 = 救急线程数目 + 核心线程数目
    在这里插入图片描述

(4):一个任务在线程池的流程

  • 线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
  • 当线程数达到corePoolSize并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue队列排
    队,直到有空闲的线程。
  • 如果队列选择了有界队列,那么任务超过了队列大小时,会创建maximumPoolSize-corePoolSize数目的
    线程来救急。
  • 如果线程到达maximumPoolSize仍然有新任务这时会执行拒绝策略。拒绝策略jd提供了4种实现,其它
    著名框架也提供了实现
    • AbortPolicy让调用者抛出RejectedExecutionException异常,这是默认策略
    • CallerRunsPolicy让调用者运行任务
    • DiscardPolicy放弃本次任务
    • DiscardOldestPolicy放弃队列中最早的任务,本任务取而代之
    • Dubbo的实现,在抛出RejectedExecutionException异常之前会记录日志,并dump线程栈信息,方便定
      位问题
    • Netty的实现,是创建一个新线程来执行任务
    • ActiveMQ的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
    • PinPoint的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略
    • 当高峰过去后,超过corePoolSize的救急线程如果一段时间没有任务做,需要结束节省资源,这个时间由
  • keepAliveTime和unit来控制。

2:固定容量的线程池(newFixedThreadPool)

(1):构造函数

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

(2):特点

  • 核心线程数==最大线程数
    (没有救急线程被创建),因此也无需超时时间
  • 阻塞队列是无界的,可以放任意数量的任务
  • 评价
    适用于任务量已知,相对耗时的任务

(3):代码示例


public class text02 {
    public static void main(String[] args) {

        ExecutorService executorService1 = Executors.newFixedThreadPool(2);

        //固定线程数  核心线程数 == 最大线程数  无救急线程 并且线程不会关闭

        //线程池里开启一个线程 并执行一个任务
        executorService1.execute(() -> {
            System.out.println(Thread.currentThread().getName() + "->1");
        });

        //线程池再开启一个线程  并执行一个任务
        executorService1.execute(() -> {
            System.out.println(Thread.currentThread().getName() + "->2");
        });

        //此时又来了一个任务,因为前面的任务执行时间比较短,所以再来任务的话,不会进入阻塞队列
        //而是会用前面已经开启的线程继续执行任务
        executorService1.execute(()-> {
            System.out.println(Thread.currentThread().getName()+"->3");
        });


    }
}

在这里插入图片描述

3:可缓存的线程池

(1):构造函数

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

(2):特点

  • 核心线程数为0,最大线程数为Interger.MAX_VALUE,救急线程的空闲存活时间是60s
    • 没有核心线程 全部都是救急线程
    • 救急线程可以无线创建
  • 队列采用了SynchronousQueue实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手
    交货)
  • 评价
    整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1分钟后释放线
    程。==适合任务数处比较密集,但每个任务执行时间较短的情况 ==(如果任务时间比较长,那么就会不断创建线程,消耗系统性能)

4:单线程池

(1):构造函数

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

(2):特点

  • 核心线程数为1,最大线程数也为1,这个线程一直存活,不会释放
  • 队列是无界队列,当任务数目大于1的时候,任务会被放入无界队列
  • 和普通单线程的区别
    自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建
    一个线程,保证池的正常工作
  • 和固定容量的线程池只开辟一个线程的区别
    Executors…newSingleThreadExecutor()线程个数始终为1,不能修改
    FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不
    能调用ThreadPoolExecutor中特有的方法
    Executors.newFixedThreadPool(1)初始时为l,以后还可以修改
    对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改

5:固定数量线程池…和ThreadPoolExecutor的区别联系

(1)如何创建固定数量的线程池,可缓存的线程池,单线程池

	 ExecutorService executorService1 = Executors.newFixedThreadPool(5);
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        ExecutorService executorService3 = Executors.newCachedThreadPool();

(2)和ThreadPoolExcutor的联系

我们将对应的线程池点进去,发现其实底层还是靠ThreadPoolExcutors来实现
在这里插入图片描述

5:自定义线程池

(1):自定义线程池的规则

我们采用 ThreadPoolExcutors来创建,这使阿里爸爸开发手册中明文推荐的,主要是其他线程池实现类都有OOM(内存用完了)的风险
在这里插入图片描述

(2):关于线程池的 Executor工具类的execute()方法

  • 当有新的任务要处理时,先看线程池中的线程数量是否大于corePoolSize,再看缓冲队列workQueue是否满,最后看线程池 中的线程数量是否大于maximumPoolSize
    另外,当线程池中的线程数量大于corePoolSize时,如果里面有线程的空闲时间超过了keepAliveTime,就将其移除线程池
  • 也就是excutor()方法中会创建线程执行任务,但是这个线程的数量是有限的,虽然数量有限,但是核心线程可以重复执行任务,救急线程在空闲存活时间的里也可以执行任务

(3):代码示例


public class text03 {
    public static void main(String[] args) {

        Executors.newCachedThreadPool();
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                            2,//核心线程数
                            5,//最大线程数 = 核心线程数 + 救急线程数(当阻塞队列)
                            3,//救济线程的空闲存活时间
                            TimeUnit.MINUTES,//时间的单位
                            new LinkedBlockingQueue<>(3),//阻塞队列默认为无界队列但是我将其设置为3
                            Executors.defaultThreadFactory(),
                             new ThreadPoolExecutor.AbortPolicy()//拒绝策略,一旦超过最大线程数就抛异常
        );


        //我们在一个for循环内
        //当我们同时来了9个任务的时候,会有2个任务去核心线程去执行,还有3个任务进入阻塞队列,还有3个任务会去救急线程那去执行
        //那么我们的还有一个线程无法执行,此时会采取拒绝策略 ;
        //我们阻塞队列中的任务是等着  救急线程 或者 核心线程去执行完手里的任务去执行的
        for (int i = 1; i <= 9; i++) {
            pool.execute(()->{
                System.out.println(Thread.currentThread().getName()+"--->"+"ok");
            });
        }





    }
}

在这里插入图片描述

(4):关于拒绝策略

  • new ThreadPoolExecutor.AbortPolicy() 这个是默认的拒绝策略 是抛出异常的
  • new ThreadPoolExecutor.CallerRunsPolicy() 这个是让调用者去执行这个任务
  • new ThreadPoolExecutor.DiscardPolicy() 这个是直接丢掉任务 不抛异常
  • new ThreadPoolExecutor.DiscardOldestPolicy() 丢掉最早执行的任务 然后再执行本任务

6:关闭线程池

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天向上的菜鸡杰!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值