当前位置: 首页 > news >正文

面试—多线程

目录

线程的创建方式

线程的生命周期

线程同步的方法

多线程内存可见性

线程安全问题


线程的创建方式

  1. 继承Therad类

    定义一个类继承Therad类

    重写run()方法(线程实际执行的逻辑)

    创建类的对象,调用start()方法开启线程

  2. 实现Runnable接口

    定义一个类实现Runnable接口,并且实例化

    重写run()方法

    创建一个Therad对象,将实现Runnable接口的类作为实参

    调用start()方法开启线程

    • 相较于继承Therad类

    • 在多个线程执行相同逻辑时,继承Thread类需要为每个线程创建一个Therad线程,重复写执行的代码

    • 而实现Runnable接口只需将实现接口的类实例化,作为参数传个Therad类

  3. 实现Callable接口和Future接口

    创建一个类实现Callable接口,接口泛型表示返回值的类型

    重写call()方法

    创建FutureTask对象用于管理返回值(参数为实现Callable接口的实例化对象)

    创建Thread对象用于启动线程(参数为futuerTask对象)

  4. 利用线程池创建

    线程池的七个参数

    1. 核心线程数,即使没有任务,也会有的线程数量

    2. 最大线程数,当任务队列已满且新任务提交时,会创建新线程直到最大值

    3. 空闲时间,当线程数量超过核心线程数时,多余的空闲线程被终止的时间

    4. 空闲时间单位

    5. 阻塞队列

      当队列为空时,获取元素的线程会被阻塞,直到有元素

      当队列已满时,插入元素操作的线程会被阻塞,直到队列中有空间

    6. 线程工厂(创建线程的方式)

      线程工厂(ThreadFactory)是一个用于创建线程的工具接口,接口只有一个方法,接受一个Runnable对象作为参数,表示线程要执行的任务,并返回创建一个Thread对象

    7. 拒绝策略,当任务队列满且线程数量超过最大线程数时,多余任务的处理策略

      终止策略(AbortPolicy)任务无法提交,直接抛出异常

      调用者运行策略(CallerRunsPolicy)由提交这个任务的线程去执行这个任务

      丢弃策略(DiscardPolicy)直接丢弃任务,不抛出异常

      丢弃最旧策略(DiscardOldestPolicy)丢弃队列中最旧的未处理的任务,将新任务加入队列

      new ThreadPoolExecutor.AbortPolicy() // 拒绝策略

线程的生命周期

  • 线程是程序执行的最小单位,是进程中的一个执行路径

  • 多线程是指在一个进程中执行多个线程,每个线程可以独立执行不同的任务

  1. 新建,创建Thread对象时,线程处于新建阶段

  2. 就绪,调用start()方法,线程处于就绪状态,等待CPU分配时间切片(操作系统分配给可执行线程的处理器时间)

  3. 运行,拿到CPU分配的时间切片,执行run()方法里面的代码

  4. 阻塞,当其他线程拿到锁执行代码时,线程处于阻塞状态

  5. 等待,调用wait()方法,线程处于等待状态,等待其他线程调用notify()方法唤醒

  6. 超时等待,当调用sleep(),wait()方法,参数是超时时间,线程进入超时等待状态,如果长时间没有被唤醒,线程会被自动恢复执行

  7. 终止,执行完run()方法里面的代码时,或出现异常,线程会被终止

线程同步的方法

  1. synchronized关键字

    修饰方法时,表示这个方法被加锁,只能由一个线程去执行,其他线程等待这个线程执行完毕释放锁,才能由下一个线程执行

  2. ReentrantLock类

    提供比synchronized更方便的方法,但是需要手动上锁(lock)和释放锁(unlock)

    可以调用tryLock()方法尝试获取锁,如果获取不到就直接返回,不去排队阻塞。

    可以去中断等待的线程,当线程等待锁的过程中被其他线程中断时,会直接中断并抛出异常

    • 采用公平锁,获取锁的顺序按照线程的请求顺序

    • 等待时间可以预测,大致就是上一个线程所运行的时间

  3. CountDownLatch类

    主要用于一个或多个线程等待其他线程

    初始化一个计数器

    当线程执行完代码后调用countDown()方法减少计数器的值

    其他线程会执行awit()方法进入阻塞状态,直到计数器的值为零

    将所有的等待线程释放

  4. 信号量(Semaphore)

    • 用于控制同时访问特定资源的线程数量

    创建一个信号量,设置许可数量

    当线程访问共享资源的时候,会先获取信号量,如果许可数大于0,则可以去访问共享资源,并且许可数减一

    当线程完成对共享资源的操作后,会释放资源,许可数加一。

多线程内存可见性

  • 在多线程环境下,线程之间可能存在缓存,当一个线程对公共变量修改时,其他线程不能马上看到这个修改的值。

  1. volatile 关键字

    volatile关键字修饰的变量可以确保修改后其他线程立即可见

    当变量被修改时,会将修改后的值放入主内存中,其他线程缓存中的变量会失效,当使用变量时,必须从主内存中获取

  2. 使用synchronized关键字或Lock锁

    当线程进入同步代码块或方法中时,会获得一个锁,强制该线程从主内存中获取共享变量,当线程释放锁时,会将修改后的共享变量写回主内存

  3. 使用原子类

    在多线程环境下对单个数据进行原子化操作

    1. 底层处理器会提供原子指令,如CAS指令,当执行CAS指令时,会在内存上发出一个信号,表示内存位置值正在被修改,将其他线程中的缓存副本标记无效,当其他使用共享变量时,从主内存中重新获取变量

      CAS主要有三个参数,内存位置(V)、期望原值(A)、新值(B)

      1. 先读取内存位置中的值(V),作为期望原值(A)

      2. 通过计算得到新值(B)

      3. 再次读取内存位置中的值,与期望原值进行比较

      4. 如果相同,说明值没有被其他线程修改,更新内存位置中的值为计算出来的新值(B)

        如果不同,说明值被修改了,重写读取,直到尝试读取一点的次数或比较相同为止

    2. 内置屏障强制处理器按指定的顺序执行内存操作,并保证不同线程对内存的修改能被其他线程看到(缓存中的变量失效)

    3. 使用乐观锁,当CAS指令出现ABA问题时

      如何一个值从A变为B,在从B变回A,CAS认为这个变量没有被修改过

      这时使用乐观锁的版本号机制,避免ABA问题

线程安全问题

  1. 线程之间抢占执行和随机调度

    • 线程的执行顺序是随机的

    使用synchronized关键字或Lock锁,保证方法和同步代码块由一个线程执行时上锁,其他线程等待

    使用原子类,对数据类型或对象进行原子操作,确保不受线程抢占调度的影响

  2. 多个线程同时修改同一变量

    使用synchronized关键字或Lock锁

  3. 死锁问题

    • 每个线程在持有资源的同时,还在等待另一个线程释放资源

    避免嵌套锁,一个线程拥有一个锁的同时避免再去获取另一个锁。或者采用固定的顺序

  4. 内存可见性

    • 在多线程环境下,一个线程对共享变量修改时,其他线程不能马上看到修改后的变量

    使用volatile关键字,在一个线程修改共享变量后,将修改后的放入主内存中,其他线程中的缓存失效,必须去主内存中获取

  5. 指令重排序

    • 一个线程在执行过程中可能先去执行别的指令,再执行写入操作。如果一个线程里面的数据会依赖另一个线程的数据结果,当CPU调度时,可能第一个线程先执行完,获得的数据不正确(获得初始化的值)

    使用volatile关键字,被修饰的变量在编译器执行的时候限制指令重排序,会按照变量的读写顺序执行

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 7款实用电脑办公软件和网站,工作效率飞跃提升
  • 速响低代码平台:升级营销管理系统,开启高效无忧新体验!
  • 快捷创建MySQL实例与配置数据库
  • 从数据仓库到数据中台再到数据飞轮:社交媒体的数据技术进化史
  • Oracle 数据库部署与实施
  • 双天线+ins
  • linux-安全管理-用户认证
  • 计算机毕业设计汽车选购销售平台管理系统车辆用户分类订单评价购买/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序
  • Java通配符的作用
  • MDPI期刊为何连年预警?2024中科院预警名单中,为何MDPI最多?本期详解!
  • 【HarmonyOS】鸿蒙头像上传-(编辑个人信息页- 头像上传)+实时数据更新
  • 解决内存8G但是需要读取一个几百G的文件到内存的方法
  • Wordpress右下角表单弹出插件
  • 02 基于STM32的按键控制继电器驱动电机
  • 低代码开发平台系统架构概述
  • 【刷算法】从上往下打印二叉树
  • Bootstrap JS插件Alert源码分析
  • egg(89)--egg之redis的发布和订阅
  • ES6核心特性
  • Git同步原始仓库到Fork仓库中
  • Protobuf3语言指南
  • zookeeper系列(七)实战分布式命名服务
  • 分布式熔断降级平台aegis
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 记一次删除Git记录中的大文件的过程
  • 前端存储 - localStorage
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 三分钟教你同步 Visual Studio Code 设置
  • 微信小程序实战练习(仿五洲到家微信版)
  • 为视图添加丝滑的水波纹
  • 小程序01:wepy框架整合iview webapp UI
  • 再谈express与koa的对比
  • mysql面试题分组并合并列
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ​比特币大跌的 2 个原因
  • ​低代码平台的核心价值与优势
  • # centos7下FFmpeg环境部署记录
  • #QT(TCP网络编程-服务端)
  • #Z0458. 树的中心2
  • #数据结构 笔记一
  • #微信小程序(布局、渲染层基础知识)
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (6)添加vue-cookie
  • (day6) 319. 灯泡开关
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (二)Kafka离线安装 - Zookeeper下载及安装
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (亲测成功)在centos7.5上安装kvm,通过VNC远程连接并创建多台ubuntu虚拟机(ubuntu server版本)...
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (原創) 未来三学期想要修的课 (日記)
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET 漏洞分析 | 某ERP系统存在SQL注入
  • .net 设置默认首页