线程安全问题的产生条件、解决方式
1、线程安全的产生条件
■ 线程安全问题概念:
多个线程在 并发 下执行,对 共享数据 进行访问,造成执行结果 不一致 的情况。
- 线程安全产生前提: 存在多个线程 、 并发执行 (线程之间处于争抢资源的竞争状态)、 共享数据
-
线程不安全造成的结果:
数据不一致
线程安全结果:数据一致;线程不安全结果:数据不一致
线程安全 问题 :就是线程不安全导致的问题
■ 并发、并行
-
并发(多个线程操作同一个资源)
- CPU 一核 ,模拟出多条线程, CPU 快速交替实现 ,多个线程之间处于竞争关系,争抢资源
-
并发(多个人一个起走)
- CPU 多核 ,多个线程可以同时执行,线程池
2、解决线程安全的方式
解决思路:破解产生的三个条件即可
比如使用单线程;对共享数据处理【比如数据类型是不变的、已知的线程安全的变量(线程安全集合Vector、原子操作类Atomic等)、线程私有的变量等】;对并发处理为同步【比如互斥同步、非阻塞同步】
- 同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条线程使用。
■ 常见的线程安全的解决方式
(1) 加锁方式
- 使用 同步关键词synchronized 或者 lock 的子类可重入锁ReentrantLock
- 互斥同步面临的主要问题是进行 线程阻塞和唤醒 所带来的性能开销,因此这种同步也被称为阻塞同步。【种悲观的并发策略】
-
高并发场景
,建议使用 Lock锁
,Lock锁要比使用Synchronize关键字在性能上有极大的提高,而且
Lock锁底层就是通过AQS+CAS机制实现的
,而CAS 也是解决线程安全的另外一种方式。
★ 说说lock 和 synchronized 锁的区别
-
synchronized 是一个
关键字
,使用C++实现的, 没办法控制锁的开始、锁结束,也没办法中断线程的执行 -
而 lock 是
java层面的实现
, 可以获取锁的状态,开启锁,释放锁,通过设置可以中断线程的执行,更加灵活 -
是否自动是否锁:synchronized 会自动是否锁,而 lock 需要手动调用unlock 方法释放,否则会死循环
lock.lock();//其他没有拿到锁的线程?阻塞 卡着不动 boolean res = lock.tryLock(1000, TimeUnit.MILLISECONDS);//一秒之后如果没有拿到锁,就返回false lock.lockInterruptibly();//中断方法
(2) 乐观并发策略
-
CAS 乐观并发策略,无锁机制
-
像线程安全的 原子操作类Atomic 底层就是使用 CAS 思想 ,只是落地实现是依赖 Unsafe 的CPU 原语级别的汇编操作
new AtomicInteger().getAndAdd(1);//获取到当前值并加1 // 底层实现 public final int getAndAdd(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta); } public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
工作中,不建议使用Unsafe 类,类名就提示你了"不安全"!
- 基于 冲突检测 的乐观并发策略,通俗地说就是不管风险, 先进行操作 ,如果没有其他线程争用共享数据,那操作就直接成功了;如果共享的数据的确被争用,产生了 冲突 ,那再进行其他的补偿措施,最常用的补偿措施是不断地 重试 ,直到出现 没有竞争的共享数据为止。
-
使用乐观并发策略需要“硬件指令集的发展”?因为我们必须要求
操作和冲突检测这两个步骤具备原子性
。靠什么来保证原子性?==>cpu 指令
-
CAS 可能出现的问题:死循环、 ABA 问题
-
ABA 问题的解决:带有 标记的
原子引用类 AtomicStampedReference
==> 类似思想"乐观锁,加版本号"
-
ABA 问题的解决:带有 标记的
(3) 线程私有局部变量
- 线程本地存储 ,比如 threadLocal,线程私有的局部变量,避免的共享变量的竞争
(4) 其他方式
-
使用 volatile
,利用它的可见性,禁止指令重排的特性,但原子性没法保证。在多线程下 没有严格的写操作冲突同步要求 ,推荐使用。常用的场景是: 使用volatile变量控制线程的终止 。
-
写是复制--CopyOnWriteArrayList
CopyOnWriteArrayList是JUC包提供的线程安全的List。
3、总结常用的解决线程安全方式
- 加锁:sync、lock
- (无锁)乐观并发:CAS
- 原子操作类:atomic
- (线程局部变量)变量不共享:threadlocal
- volatile
如果本文对你有帮助的话记得给一乐点个赞哦,感谢!
「其他文章」
- 线程池底层原理详解与源码分析
- 30分钟掌握 Webpack
- 线性回归大结局(岭(Ridge)、 Lasso回归原理、公式推导),你想要的这里都有
- 【前端必会】webpack loader 到底是什么
- 中心化决议管理——云端分析
- HashMap底层原理及jdk1.8源码解读
- 详解JS中 call 方法的实现
- 打印 Logger 日志时,需不需要再封装一下工具类?
- 初识设计模式 - 代理模式
- 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码
- Springboot之 Mybatis 多数据源实现
- CAS核心思想、底层实现
- 面试突击86:SpringBoot 事务不回滚?怎么解决?
- 基于electron vue element构建项目模板之【打包篇】
- MiniWord .NET Word模板引擎,藉由Word模板和数据简单、快速生成文件。
- 认识线程,初始并发
- 1-VSCode搭建GD32开发环境
- 初识设计模式 - 原型模式
- 线程安全问题的产生条件、解决方式
- 2>&1到底是什么意思?