八股文 --- 线程并发

进程, 线程, 协程

在java中没有协程, 其并发模型主要基于线程

线程和进程

  • 进程: 系统运行的基本单位, 独立占用输入输出, 内部包含多个线程, 一个进程对应一个软件, 开销大
  • 线程: 进程内部的并发执行, 更小粒度, 独立运行的最小单位

一个进程内可以存在多个并发线程

  • 在你任务管理器里面看到的就是进程, 一般一个应用程序对应一个进程
  • java的Main函数就是开启一个进程, 而thread是开启子线程
  • java的并发是基于子线程实现的, 而不是多个Main函数

在高并发项目中, 依旧只有一个Main, 而是通过多个thread启动了多个子线程来实现并发执行

在分布式项目中, 存在多个Main, 在不同硬件软件服务器上实现不同功能, 经常与微服务相联合 [ 一般微服务就是分布式项目 ]

线程与进程数据结构

一个进程中可以有多个线程,多个线程共享进程的方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器虚拟机栈本地方法栈

并发不是并行

  • 并发:两个及两个以上的作业在同一 时间段 内执行。
  • 并行:两个及两个以上的作业在同一 时刻 执行。

实际上并发是同时间段执行, 并不是完全的同时执行

同步与异步区别

  • 同步:发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
  • 异步:调用在发出之后,不用等待返回结果,该调用直接返回。

需要进行并发, 一般就是通过异步调用开启多个线程

java线程和计算机组成原理中的线程关系

java线程本质是操作系统的线程,  [ 操作系统线程不会让计算机CPU转换到管态, 而是要操作系统管理和控制 ]

  • 用户线程:由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)。
  • 内核线程:由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)。

然后java线程算内核线程

保证并发安全三大特征

原子性, 可见性, 有序性

线程上下文不仅仅是上下文

线程上下文切换是一个线程被占用时, CPU切换到另外一个线程

线程池参数

  • 核心线程数:线程池中的基本线程数量
  • 最大线程数:当阻塞队列满了之后,逐一启动
  • 最大线程的存活时间:当阻塞队列的任务执行完后,最大线长的回收时间
  • 最大线程的存活时间单位
  • 阻塞队列:当核心线程满后,后面来的任务都进入阻塞队列
  • 线程工厂:用于生产线程
  • 任务拒绝策略:阻塞队列满后,拒绝任务,有四种策略:
    • (1)抛异常
    • (2)丢弃任务不抛异常
    • (3)打回任务
    • (4)尝试与最老的线程竞争

死锁的条件

  • 循环等待: 线程对资源的等待可以形成一个环
  • 互斥: 线程之间是互斥的, 一个执行另外一个不能执行
  • 不可抢占: 线程的资源一旦分配不能中断
  • 请求保持: 线程一直请求资源, 未获取到前不会停止

针对死锁的预防就是从这四个方面下手, 破坏其中一个条件即可

  • 破坏请求与保持条件:一次性申请所有的资源。
  • 破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

线程的暂停

sleep和wait两个方法都可以暂停线程, 但是其使用情况和作用不同
sleep方法使得进程进入阻塞, 释放CPU资源, 但是其线程占用的其他资源不会释放[ 不释放锁 ]

用于当前线程暂停让其他进程执行

wait是让线程进入等待状态, 会释放CPU和其他占用资源[ 释放锁 ]
让其他线程使用资源

线程开启方式

  • 使用thread
  • 实现runnable接口
  • 实现 Callable 接口:带有返回值
  • 线程池创建线程

CAS

是什么

不是关键字, 而是一个算法思想

包含三个操作数——内存位置(V)、期望的原值(A)和新值(B)。如果内存位置V的值与期望的原值A相等,那么处理器会自动将该位置值更新为新值B。否则,处理器不做任何操作。无论哪种情况,它都必须在CAS指令结束之前返回该位置的值。

一种乐观锁机制,  但是不涉及线程的调用和顺序

当多个线程执行时, 会按照线程执行的快慢 "抢" 执行线程, 然后更新

同一时间对同一个数据只能被一个线程抢到更新, 其他线程全失败, 然后重新尝试

存在ABA问题, 就是一个变量在初次读取和准备赋值时读取结果相同, 但是可能被进行了两次修改

即 读取时为A 之后改为了B 又改回了A, 此时赋值可以执行, 进行的另外操作没有记录

解决方法是加版本号与时间戳

Synchrpnized关键字 加锁

是什么

来实现锁的功能的,它可以确保同一时间只有一个线程可以执行特定的代码块或方法。

使用

  • 修饰方法 [ 锁对象实例 ]
  • 修饰静态方法 [ 锁类 ]
  • 修饰代码块 [ 锁指定对象 ]

通过对子线程使用的方法, 类加SynchronizedUsed, 实现线程安全

就是防止两个子线程同时刻执行一个方法导致数据混乱

作用: 在同步线程中, 防止同时执行

用该关键字修饰的方法, 对象, 在同一时刻只能有一个线程进入那个方法, 一个线程访问对象

注意项

  • 构造方法不能修饰
  • 底层涉及对象监视器

底层原理

Synchrpnized是基于对象监视器, 实际上是基于操作系统的并发控制

在使用时需要从用户态切换到核心态[ 目态切换到管态 ]

成本很高

优化

在jdk1.6后, 对其进行了优化

  • 偏向锁:当一段代码没有别的线程访问,此时线程去访问会直接获取偏向锁
  • 轻量级锁:当锁是偏向锁时,有另外一个线程来访问,会升级为轻量级锁。线程会通过CAS方式获取锁,不会阻塞,提高性能,
  • 重量级锁:轻量级锁自旋一段时间后线程还没有获取到锁,会升级为重量级锁,重量级锁时,来竞争锁的所有线程都会阻塞,性能降低

注意,锁只能升级不能降级

Threadlocal

是线程的变量管理

类似vue里面的VUEX
VueX是全局共享, threadlocal是局部独立

实现线程之间数据的隔离, 减少开销, 增加内聚

原理

原理是为每个线程创建变量副本,不同线程之间不可见,保证线程安全。每个线程内部都维护了一个Map,key为threadLocal实例,value为要保存的副本。

可能内存泄漏

如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。

因为threadlocal的数据结构问题 [ threadlocalMap ] , 在垃圾回收时只会回收掉 threadlocal的 key, 而value会保存

解决方法是显示调用 remove方法来移除关联 threadlocal变量

原子类

在java中, 原子类变量是一类具有原子性操作的变量,这意味着原子变量的操作不会被线程调度机制打断,一旦开始,就会一直运行到结束,中间不会切换到任何别的进程。[ 类似的原子不可切分 ]

有基础数据类型的原子类, 数组类型, 引用类型

作用

保证线程安全, 效率高

原子类可以保证同一时间只会被一个线程修改

在性能上存在优势

可以替代在子线程中手动添加锁的操作

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值