JUC并发编程(五):快速了解线程池

时光毁灭记忆、已成空白 提交于 2020-03-07 18:43:33

池化技术
首先我们要了解的一个东西就是程序运行的本质是占用系统资源!
所以我们为了提高程序的使用效率,降低我们的一个性能消耗,就要把一些频繁创建的资源提前给准备好,这就是池化技术.

常见的有线程池,连接池,内存池,对象池…

其次我们为什么要用线程池-- 多路复用

关于线程池,我们只要掌握三大方法,七大参数,四种拒绝策略即可!下

三大方法
newFixedThreadPool(),newCachedThreadPool(),newSingleThreadExecutor()

public class Test1 {
    public static void main(String[] args) {
        //线程池 Executors原生三大方法
        //固定大小
        ExecutorService threadpool1= Executors.newFixedThreadPool(5);
        //弹性收缩
        ExecutorService threadpool2=Executors.newCachedThreadPool();
        //只有一个
        ExecutorService threadpool3=Executors.newSingleThreadExecutor();

        try {
            for (int i = 1; i <= 10; i++) {
                //线程池,执行线程
                threadpool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"running...");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池关闭
            threadpool3.shutdown();
        }
    }
}

这里值得注意的是,我们创建线程池一般不会使用Excutors去创建,原因阿里巴巴开发手册上也有给出说明:
在这里插入图片描述

七大参数
我们先通过源码看下

new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 约等于21亿
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());

new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>())

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;
}

下面我们就银行为例,具体的去讲解这七大参数:
在这里插入图片描述
** int corePoolSize**:核心池大小,对应的就是上图中的绿色的①②两个窗口,这两个窗口不管是高峰期还是平时都会开,平常如果①②窗口有人办理,其它来的人都会进入候客区等待,并不会开③④⑤窗口,也就是它的一个核心处理能力是同时只能有两个线程处理任务.
int maximumPoolSize:最大线程数,对应的就是图中红色的③④⑤窗口,这个平时是不开的,只有在高峰期才开的,也就是最大的处理能力就是同时有5个线程处理任务
long keepAliveTime:超时等待时间,也就是高峰期过后,超过多少时间还没有人来的话,③④⑤窗口就会关闭只保留核心的线程.
TimeUnit unit:时间单位,即超时等待的时间是以什么为单位.
BlockingQueue workQueue:阻塞队列,相当于我们的候客区,当柜台上有人办理业务的时候,我们就在候客区等待.
ThreadFactory threadFactory:创建线程的工厂
RejectedExecutionHandler handler:拒绝策略,即当银行的的所有窗口都有人在办理业务,并且候客区也满了的时候,这时如果再有人过来办理业务我们该采取何种方式拒绝.

public class Test2 {
    public static void main(String[] args) {
        ExecutorService threadPool=new ThreadPoolExecutor(
                2,//核心线程数
                5, //线程池最大大小
				2L, //超时等待时间
                TimeUnit.SECONDS,//时间单位
                //阻塞队列
                new LinkedBlockingDeque<>(3),
                //不用变
                Executors.defaultThreadFactory(),
                //拒绝策略
                new ThreadPoolExecutor.AbortPolicy()
        );

     
   
        try {
            for (int i = 1; i <= 10; i++) {
                //默认在处理
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"running");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

四大策略

  • 1、ThreadPoolExecutor.AbortPolicy(); 抛出异常,丢弃任务
  • 2、ThreadPoolExecutor.DiscardPolicy();不抛出异常,丢弃任务
  • 3、ThreadPoolExecutor.DiscardOldestPolicy(); 尝试获取任务,不一定执行!
  • 4、ThreadPoolExecutor.CallerRunsPolicy(); 哪来的去哪里找对应的线程执行!也就是如果你是公司来的,人满了你就会公司去.

为什么会有这四种策略,其实对应的是上篇文章讲的阻塞队列的四组API

注意点:最大参数该如何设置?
CPU 密集型: 通过CPU设置
IO 密集型: 磁盘读写、 一个线程在IO操作的时候、另外一个线程在CPU中跑,造成CPU空闲。最大线程数应该设置为 IO任务数! 大文件读写耗时!单独的线程让他慢慢跑。

通过代码层面去设置

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

        // 代码级别的
        System.out.println(Runtime.getRuntime().availableProcessors());

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(), // 线程池最大大小5
                2L,
                TimeUnit.SECONDS, 
                new LinkedBlockingDeque<>(3), // 根据业务设置队列大小,队列大小一定要设置
                Executors.defaultThreadFactory(), // 不用变
                new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
        );

 
        try {
            // 队列  RejectedExecutionException 拒绝策略
            for (int i = 1; i <= 10; i++) {
                // 默认在处理
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" running....");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }


    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!