JUC-线程池问题

JUC-线程池问题

  • 要了解线程池,首先先了解一下池化技术

    池化技术,提前保存大量的资源,以备不时之需以及重复使用。通俗点理解,就是实现准备好一些资源,有人要用就来这里拿,用完以后要还回来,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。对连接或线程的复用,并对复用的数量、时间等进行控制,从而使得系统的性能和资源消耗达到最优状态。

    参考文档

    什么是池化技术

  • 接下来了解一下线程池

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

  • 那么线程池有哪些好处?

    • 降低资源消耗:通过重复利用现有的线程来执行任务,避免多次创建和销毁线程。
    • 提高响应速度:因为省去了创建线程这个步骤,所以在拿到任务时,可以立刻开始执行。
    • 提供附加功能:线程池的可拓展性使得我们可以自己加入新的功能,比如说定时、延时来执行某些线程。
  • 关于线程池的创建,首先看一下JUC下Executors工具类的三大方法
    在这里插入图片描述
    查看API可知,java.util.concurrent.Executors下有三个基本的创建线程池的方法

  • ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程

import java.util.concurrent.*;

public class Executor01 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
        try {
            for (int i = 0; i < 100; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

  • ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小
public class Executor02 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);  //创建一个固定的线程池的大小
        try {
            for (int i = 0; i < 20; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

  • ExecutorService threadPool = Executors.newCachedThreadPool(); //可伸缩的,可根据实际线程数进行调整
public class Executor03 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 20; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

在这里插入图片描述
这个方法的结果具有不确定性,测试的次数、线程的数量和电脑的性能都可能会对结果产生影响

  • 在阿里巴巴开发手册中明确表明,线程池的创建不允许使用Executor,而是使用ThreadPoolExecutor,原文如下
    在这里插入图片描述
    那么,ThreadPoolExecutor是什么呢?

    我们先来看一波源码

    ExecutorService threadPool = Executors.newSingleThreadExecutor();
            /**
             * public static ExecutorService newSingleThreadExecutor() {
             *             return new Executors.FinalizableDelegatedExecutorService
             *                     (new ThreadPoolExecutor(1, 1,
             *                             0L, TimeUnit.MILLISECONDS,
             *                             new LinkedBlockingQueue<Runnable>()));
              */
    
    ExecutorService threadPool = Executors.newFixedThreadPool(5);
            /**
             * public static ExecutorService newFixedThreadPool(int nThreads) {
             *         return new ThreadPoolExecutor(nThreads, nThreads,
             *                                       0L, TimeUnit.MILLISECONDS,
             *                                       new LinkedBlockingQueue<Runnable>());
             */
    
    ExecutorService threadPool = Executors.newCachedThreadPool();
            /**
             *  public static ExecutorService newCachedThreadPool() {
             *             return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  //21亿  OOM溢出
             *                     60L, TimeUnit.SECONDS,
             *                     new SynchronousQueue<Runnable>());
             */
    

    上面三组分别为Exector三大方法的源码片段,从中可以看出,三个方法都调用了ThreadPoolExecutor方法

    下面来看一下ThreadPoolExecutor方法的源码

     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.acc = System.getSecurityManager() == null ?
                    null :
                    AccessController.getContext();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
             this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    

    由源码可知,ThreadPoolExecutor方法中包含了7个参数,下面通过几个场景来理解这7个参数的含义

场景一:开启了2个窗口,有三个窗口是关闭的,但是候客区还没有坐满

1、int corePoolSize代表核心线程池大小,即当前场景下开通的窗口数量:2
在这里插入图片描述
场景二:窗口1、2满了,候客区也坐满了,但是还有人要办理业务

2、 int maximumPoolSize代表最大核心线程池大小 ,即能够开启的最大的窗口数:5
在这里插入图片描述在这里插入图片描述
场景三:当业务都办理完毕,后续再没有人来办理业务的时候

这种场景下,在超过一定时间没有人访问的情况下,后开启的线程会被释放,回到核心线程池大小:5 -> 2

3、long keepAliveTime代表超时了没有人调用就会释放
4、TimeUnit unit代表超时单位

即超过设定的时长就会被释放掉

5、BlockingQueue workQueue代表阻塞队列,此处即候客区
6、ThreadFactory threadFactory代表线程工厂,创建线程的,一般不用动

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

场景四:当窗口办理人员已满,候客区已满,还有用户要办理业务

7、RejectedExecutionHandler handler //拒绝策略
在这里插入图片描述
拒绝策略,即当前场景下,对于再进入办理业务的人员的处理策略,共有4种拒绝策略
在这里插入图片描述
下面用具体的实例来演示一下

  • 策略一:new ThreadPoolExecutor.AbortPolicy()
import java.util.concurrent.*;

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

      ExecutorService threadPool = new ThreadPoolExecutor(
                2,                                 //核心线程池大小(int corePoolSize)
                5,                                 //最大核心线程池大小(int maximumPoolSize)
                3,                                 //超时了没有人调用就会释放(long keepAliveTime)
                TimeUnit.SECONDS,                  //超时单位(TimeUnit unit)
                new LinkedBlockingQueue<>(3),      //阻塞队列
                Executors.defaultThreadFactory(),  //线程工厂
                new ThreadPoolExecutor.AbortPolicy());//银行满了,还有人进来,不处理这个人的                                                                          //抛出异常java.util.concurrent.RejectedExecutionException
        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

  • 策略二:哪来的去哪里
import java.util.concurrent.*;

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

      ExecutorService threadPool = new ThreadPoolExecutor(
                2,                                 //核心线程池大小(int corePoolSize)
                5,                                 //最大核心线程池大小(int maximumPoolSize)
                3,                                 //超时了没有人调用就会释放(long keepAliveTime)
                TimeUnit.SECONDS,                  //超时单位(TimeUnit unit)
                new LinkedBlockingQueue<>(3),      //阻塞队列
                Executors.defaultThreadFactory(),  //线程工厂
                new ThreadPoolExecutor.CallerRunsPolicy());  //哪来的去哪里
        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

  • 策略三:队列满了,丢掉任务,不会抛出异常
import java.util.concurrent.*;

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

      ExecutorService threadPool = new ThreadPoolExecutor(
                2,                                 //核心线程池大小(int corePoolSize)
                5,                                 //最大核心线程池大小(int maximumPoolSize)
                3,                                 //超时了没有人调用就会释放(long keepAliveTime)
                TimeUnit.SECONDS,                  //超时单位(TimeUnit unit)
                new LinkedBlockingQueue<>(3),      //阻塞队列
                Executors.defaultThreadFactory(),  //线程工厂
                new ThreadPoolExecutor.DiscardPolicy());  //队列满了,丢掉任务,不会抛出异常
        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述
策略四:队列满了,尝试去和最早的竞争,不抛出异常

import java.util.concurrent.*;

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

      ExecutorService threadPool = new ThreadPoolExecutor(
                2,                                 //核心线程池大小(int corePoolSize)
                5,                                 //最大核心线程池大小(int maximumPoolSize)
                3,                                 //超时了没有人调用就会释放(long keepAliveTime)
                TimeUnit.SECONDS,                  //超时单位(TimeUnit unit)
                new LinkedBlockingQueue<>(3),      //阻塞队列
                Executors.defaultThreadFactory(),  //线程工厂
                new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,不抛出异常
        try {
            for (int i = 0; i < 10; i++) {
                //使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

野猫爱吃鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值