【高级篇】线程与线程池

  • Post author:
  • Post category:其他

一、线程回顾

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();//启动线程

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

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

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

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

2、线程池的七大参数

   /**
         * 七大参数
         * corePoolSize:[5] 核心线程数[一直存在除非(allowCoreThreadTimeOut)]; 线程池,创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行。
         *        5个  Thread thread = new Thread();  thread.start();
         * maximumPoolSize:[200] 最大线程数量;  控制资源
         * keepAliveTime:存活时间。如果当前的线程数量大于core数量。
         *      释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
         * unit:时间单位
         * BlockingQueue<Runnable> 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、线程池创建,准备好 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、开发中为什么使用线程池

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


版权声明:本文为qq_31776219原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。