• 【高级篇】线程与线程池


    一、线程回顾

    1、初始化线程的 4 种方式
    1)、继承 Thread

     public static class Thread01 extends Thread{
            @Override
            public void run() {
                System.out.println("当前线程:"+Thread.currentThread().getId());
                int i = 10 / 2;
                System.out.println("运行结果:"+i);
            }
        }
    Thread01 thread = new Thread01();
    thread.start();//启动线程
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2)、实现 Runnable 接口

    Runable01 runable01 = new Runable01();
    new Thread(runable01).start();
        public static class Runable01 implements Runnable{
    
            @Override
            public void run() {
                System.out.println("当前线程:"+Thread.currentThread().getId());
                int i = 10 / 2;
                System.out.println("运行结果:"+i);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)

             *         FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
             *          *         new Thread(futureTask).start();
             *          *         //阻塞等待整个线程执行完成,获取返回结果
             *          *         Integer integer = futureTask.get();
             
        public static class Callable01 implements Callable<Integer>{
    
            @Override
            public Integer call() throws Exception {
                System.out.println("当前线程:"+Thread.currentThread().getId());
                int i = 10 / 2;
                System.out.println("运行结果:"+i);
                return i;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4)、线程池
    * 给线程池直接提交任务。
    * service.execute(new Runable01());
    * 1、创建:
    * 1)、Executors
    * 2)、new ThreadPoolExecutor
    *
    * Future:可以获取到异步结果

    方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
    方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
    方式 4:可以控制资源,性能稳定。 通过如下两种方式初始化线程池
    我们以后再业务代码里面,以上三种启动线程的方式都不用。【将所有的多线程异步任务都交给线程池执行】
    当前系统中池只有一两个,每个异步任务,提交给线程池让他自己去执行就行

    Executors.newFiexedThreadPool(3);
    //或者
    new   ThreadPoolExecutor(corePoolSize,   maximumPoolSize,   keepAliveTime,   TimeUnit	unit, workQueue, threadFactory, handler);
    
    • 1
    • 2
    • 3

    通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一 个异步调用可能会依赖于另一个异步调用的执行结果。

    2、线程池的七大参数

       /**
             * 七大参数
             * corePoolSize:[5] 核心线程数[一直存在除非(allowCoreThreadTimeOut)]; 线程池,创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行。
             *        5个  Thread thread = new Thread();  thread.start();
             * maximumPoolSize:[200] 最大线程数量;  控制资源
             * keepAliveTime:存活时间。如果当前的线程数量大于core数量。
             *      释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
             * unit:时间单位
             * BlockingQueue workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面。
             *              只要有线程空闲,就会去队列里面取出新的任务继续执行。
             * threadFactory:线程的创建工厂。
             * RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略拒绝执行任务
             *
             *
             *
             * 工作顺序:
             * 1)、线程池创建,准备好core数量的核心线程,准备接受任务
             * 1.1、core满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行
             * 1.2、阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
             * 1.3、max满了就用RejectedExecutionHandler拒绝任务
             * 1.4、max都执行完成,有很多空闲.在指定的时间keepAliveTime以后,释放max-core这些线程
             *
             *      new LinkedBlockingDeque<>():默认是Integer的最大值。内存不够
             *
             * 一个线程池 core 7; max 20 ,queue:50,100并发进来怎么分配的;
             * 7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略。
             * 如果不想抛弃还要执行。CallerRunsPolicy;
             *
             */
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                    200,
                    10,
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<>(100000),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy());
    //        Executors.newCachedThreadPool() core是0,所有都可回收
    //        Executors.newFixedThreadPool() 固定大小,core=max;都不可回收
    //        Executors.newScheduledThreadPool() 定时任务的线程池
    //        Executors.newSingleThreadExecutor() 单线程的线程池,后台从队列里面获取任务,挨个执行
            //
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这里插入图片描述
    运行流程:
    1、线程池创建,准备好 core 数量的核心线程,准备接受任务
    2、新的任务进来,用 core 准备好的空闲线程执行。
    (1)、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
    (2)、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
    (3)、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
    (4)、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
    3、所有的线程创建都是由指定的 factory 创建的。

    面试:
    一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的;
    先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个
    被安排上了。剩下 30 个默认拒绝策略。

    3、常见的 4 种线程池

    newCachedThreadPool
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若 无可回收,则新建线程。
    newFixedThreadPool
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    newScheduledThreadPool
    创建一个定长线程池,支持定时及周期性任务执行。
    newSingleThreadExecutor
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务 按照指定顺序(FIFO, LIFO, 优先级)执行。

    4、开发中为什么使用线程池

    降低资源的消耗
    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
    提高响应速度
    因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务 的状态,当任务来时无需创建新的线程就能执行
    提高线程的可管理性
    线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来 的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使 用线程池进行统一分配

  • 相关阅读:
    常用API
    【人工智能】知识表示
    Java【并发】面试题
    基于 Python 的地理空间绘图(附源码)
    【C++】string类
    docker内存清理
    Linux 系统中查看和停止删除定时任务
    Lua5.4源码剖析:二. 详解String数据结构及操作算法
    安装python-igraph报错
    javaEE----servlet初识
  • 原文地址:https://blog.csdn.net/qq_31776219/article/details/127993204