【多线程】线程池详解,常见的面试题,以及手动实现线程池

这篇具有很好参考价值的文章主要介绍了【多线程】线程池详解,常见的面试题,以及手动实现线程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:
大家好,我是良辰丫,今天我们来学习一下线程池.线程池到底是什么呢?我们一起往下看💞💞💞

🧑个人主页:良辰针不戳
📖所属专栏:javaEE初阶
🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
💦期待大家三连,关注,点赞,收藏。
💌作者能力有限,可能也会出错,欢迎大家指正。
💞愿与君为伴,共探Java汪洋大海。

【多线程】线程池详解,常见的面试题,以及手动实现线程池

1. 什么是线程池

我们听说过字符串常量池,数据库连接池等,字符串常量池是放字符串常量的,那么,线程池,顾名思义,就是放线程的.

线程池的目的是提高效率,线程池的创建虽然比进程轻量,但是频繁创建线程的时候,也会有巨大的开销.那么我们就需要准备一个线程池,创建线程不是直接从系统申请,而是从线程池里面取,线程不用了的时候还给线程池.

线程提高效率方式.

  • 协程,这是一种轻量级线程,但是java标准库还不支持.
  • 线程池

为什么从线程池里面拿线程比从系统创建线程更高效???

从线程池拿线程,纯粹的用户态操作;而从系统创建线程,涉及到用户态和内核态之间的切换,真正的创建是要在内核态完成的.

一个操作系统 = 内核 + 配套的应用程序

  • 内核是操作系统最核心的功能模块集合,硬件管理,各种驱动,进程管理,内存管理,文件系统.
  • 内核需要给上层应用程序提供支持,应用程序调用系统内核,告诉内核,自己要进行某个操作,内核再通过驱动程序,操作显示器,完成上述功能.
  • 应用程序,同一时刻有很多个,但是内核只有一个,内核要给那么多程序提供服务,有时候服务不一定那么及时.

线程池的优点:

  • 降低资源消耗:通过重复利用已经创建的线程降低线程创建和销毁造成的消耗.
  • 提高响应速度:当任务到达的时候,任务可以不需要等待线程创建就能立即执行.
  • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,而且会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.

2. 标准库中的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test19 {
    public static void main(String[] args) {
    //下面的参数10为10个线程,下面并非直接new ExecutorService对象
    //而是通过Executors类里面的静态方法完成对象的构造(工程模式)
        ExecutorService pool = Executors.newFixedThreadPool(10);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("线程1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
        pool.submit(new Runnable() {
            @Override
            public void run() {
                while (true){
                    System.out.println("线程2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
    }
}

下面为运行结果

【多线程】线程池详解,常见的面试题,以及手动实现线程池

上述创建线程池我们用到了工厂模式,简述一下工厂模式,工厂模式在创建对象的时候,不能直接new,而是通过一些其它的方法(通常是静态方法),协助我们把对象创建出来.说白了,工厂模式就是用拉力填构造方法的坑,如果想要提供多种不同的构造对象的方式,就要基于重载.

【多线程】线程池详解,常见的面试题,以及手动实现线程池

上述代码方法签名一样,无法构成重载,这是构造方法的局限性,而工厂模式就解决了一些这样的问题,在这里就不细说了.

3. 线程池的几个关键词

接下来我们来了解线程池的几个关键词.注意,查看官方文档是我们以后在工作过程中非常重要,我们要学着并且尝试看源码以及官方文档.

//1.核心线程数
corePoolSize
//2.最大线程数
maximumPoolSize

如果当前任务比较多,线程池就会创建一些"临时线程";如果当前任务少了,比较空闲了,线程池就会把多出来的临时线程销毁掉.为了便于理解,大家也可以把核心线程数想象成正式员工,把最大线程数想象成正式员工+临时员工.

long keepAliveTime
TimeUnit unit

任务少的时候,临时线程并不会立即销毁,而是有一个存活时间.

BlockKingQueue<Runnable> workQueue

线程池要管理很多的任务(线程就可以认为是任务),我们可以手动指定线程池一个队列,此时我们就可以非常方便的控制且获取队列中的信息了.submit方法就是把任务放到该队列中

//工厂模式,创建线程辅助的类
ThreadFactory threadFactory
//线程池的拒绝策略,如果线程池线程满了,继续添加任务,就会拒绝
RejectedExecutionHandler handler

4. 标准库提供的四种拒绝策略(面试题)

【多线程】线程池详解,常见的面试题,以及手动实现线程池

  • 如果满了,继续添加任务,就会抛出异常.
  • 添加的线程自己负责这个任务.
  • 丢弃最老的任务.
  • 丢弃最新的任务.

注意:

  • 最老的认为是队列的队首元素,不执行了,直接删除.
  • 线程池没有依赖阻塞行为,而是通过额外实现了其它逻辑更好的处理,阻塞有的时候可行,有的时候不好使,线程池中不希望依赖满了阻塞,而主要利用空了进行阻塞.
  • ThreadPoolExecutor类的构造方法的参数,要重点去掌握.

5. 线程池的执行流程

【多线程】线程池详解,常见的面试题,以及手动实现线程池

  • 当新加入一个任务时,先判断当前线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务;
  • 如果结果为 true,则判断任务队列是否已满,如果结果为 false,则把任务添加到任务队列中等待线程执行.
  • 如果结果为 true,则判断当前线程数量是否超过最大线程数?如果结果为 false,则新建线程执行此任务.
  • 如果结果为 true,执行拒绝策略.

6. 手动实现线程池

  • 核心操作为 submit, 将任务加入线程池中
  • 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
  • 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增
    线程了.
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool {
    // 阻塞队列用来存放任务.
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    // 此处实现一个固定线程数的线程池.
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                try {
                    while (true) {
                        // 此处需要让线程内部有个 while 循环, 不停的取任务.
                        Runnable runnable = queue.take();
                        runnable.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

            t.start();
        }
    }
}
public class Test21 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        for (int i = 0; i < 100; i++) {
        //匿名内部类也要遵循变量捕获功能
            int number = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello " + number);
                }
            });
        }
    }

}

【多线程】线程池详解,常见的面试题,以及手动实现线程池

从运行结果大家可以发现,线程池任务的执行顺序和添加顺序不一定是相同的,因为这些线程是无序调用的.
实际工作中,线程池要创建多少线程呢?根据实际情况而定,比如CPU状况,物理逻辑等.

7. 总结多线程初阶

看到这里,多线程初阶就结束了,接下来我们总结几个面试常考的内容,希望小小的内容可以帮到大家.

7.1 如何保证线程安全

  • 使用没有共享资源的模型
  • 共享资源只读,不写( 不需要写共享资源的模型使用不可变对象)
  • 直面线程安全(保证原子性,顺序性,可见性)

7.2 线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

7.3 线程与进程的区别

-进程是系统进行资源分配和调度的一个独立单位,线程是程序执行的最小单位。文章来源地址https://www.toymoban.com/news/detail-417394.html

  • 进程有自己的内存地址空间,线程只独享指令流执行的必要资源,如寄存器和栈。
  • 由于同一进程的各线程间共享内存和文件资源,可以不通过内核进行直接通信。
  • 线程的创建、切换及终止效率更高。

到了这里,关于【多线程】线程池详解,常见的面试题,以及手动实现线程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【多线程进阶】信号量,线程安全集合类,Hashtable与ConcurrentHashMap的区别,多线程常见的面试题

    前言: 大家好,我是 良辰丫 ,今天学习多线程最后一节内容,我们主要去了解信号量,线程安全集合类,Hashtable与ConcurrentHashMap的区别,多线程常见的面试题,我们需要重点去掌握,💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,

    2023年04月27日
    浏览(53)
  • javaEE初阶——多线程(九)——JUC常见的类以及线程安全的集合类

    T04BF 👋专栏: 算法|JAVA|MySQL|C语言 🫵 小比特 大梦想 此篇文章与大家分享多线程专题的最后一篇文章:关于JUC常见的类以及线程安全的集合类 如果有不足的或者错误的请您指出! 3.1Callable接口 Callable和Runnable一样,都是用来描述一个任务的 但是区别在于 ,用Callable描述的任务是有

    2024年04月25日
    浏览(43)
  • springboot整合rabbitmq的发布确认,消费者手动返回ack,设置备用队列,以及面试题:rabbitmq确保消息不丢失

    目录 1.生产者发消息到交换机时候的消息确认 2.交换机给队列发消息时候的消息确认 3.备用队列 3.消费者手动ack   rabbitmq的发布确认方式,可以有效的保证我们的数据不丢失。   消息正常发送的流程是:生产者发送消息到交换机,然后交换机通过路由键把消息发送给对应的队

    2024年02月09日
    浏览(72)
  • 22道常见RocketMQ面试题以及答案

    面试宝典到手,搞定面试,不再是难题,系列文章传送地址,请点击本链接。 1、RocketMQ是什么? 2、RocketMQ有什么作用? 3、RoctetMQ的架构 4、RoctetMQ的优缺点 8、消息过滤,如何实现? 9、消息去重,如果由于网络等原因,多条重复消息投递到了Consumer端,你怎么进行消息去重? 1

    2024年02月10日
    浏览(47)
  • 网络安全面试题大全(整理版)300+面试题附答案详解,最全面详细

    随着国家政策的扶持,网络安全行业也越来越为大众所熟知,想要进入到网络安全行业的人也越来越多。 为了拿到心仪的Offer之外,除了学好网络安全知识以外,还要应对好企业的面试。 作为一个安全老鸟,工作这么多年,面试过很多人也出过很多面试题目,也在网上收集了

    2024年02月08日
    浏览(61)
  • 【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类

    目录 1、JUC(java.util.concurrent) 1.1、Callable 接口 1.2、ReentrantLock 可重入锁 1.3、Semaphore 信号量 1.4、CountDownLatch 这是java中的一个包,存放着 多线程编程 中常见的一些类。 【Java多线程】Thread类的基本用法-CSDN博客 https://blog.csdn.net/zzzzzhxxx/article/details/136121421?spm=1001.2014.3001.5501 往

    2024年04月10日
    浏览(86)
  • 20道常见的kafka面试题以及答案

    JAVA面试宝典,搞定JAVA面试,不再是难题,系列文章传送地址,请点击本链接。 目录 1、kafka的消费者是pull(拉)还是push(推)模式,这种模式有什么好处? 2、kafka维护消息状态的跟踪方法 3、zookeeper对于kafka的作用是什么? 4、kafka判断一个节点还活着的有那两个条件? 5、讲一讲

    2024年02月03日
    浏览(32)
  • 网络安全面试题大全(整理版)500+面试题附答案详解,最全面详细,看完稳了

    随着国家政策的扶持,网络安全行业也越来越为大众所熟知,想要进入到网络安全行业的人也越来越多。 为了拿到心仪的Offer之外,除了学好网络安全知识以外,还要应对好企业的面试。 作为一个安全老鸟,工作这么多年,面试过很多人也出过很多面试题目,也在网上收集了

    2024年02月09日
    浏览(52)
  • 【JavaEE】JUC(java.util.concurrent)的常见类以及线程安全的集合类

    目录 1、JUC(java.util.concurrent)的常见类 1.1、Callable接口的用法(创建线程的一种写法)  1.2、ReentrantLock可重入互斥锁 1.2.1、ReentrantLock和synchronized的区别  1.2.2、如何选择使用哪个锁 1.3、Semaphore信号量 1.4、CountDownLatch  2、线程安全的集合类 2.1、多线程环境使用ArrayList  2.2、

    2024年02月07日
    浏览(49)
  • kubernetes常见面试问题详解

    在面试的时候,面试官常常会问一些问题: k8s是什么?有什么用? k8s由哪些组件组成? pod的启动流程? k8s里有哪些控制器? k8s的调度器里有哪些调度算法? pod和pod之间的通信过程? 外面用户访问pod数据流程? 你常用哪些命令? 容器的类型? 3种探针? pod的升级? HPA、V

    2024年02月10日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包