为什么会有线程不安全
首先,线程不安全原理上是由于共享变量不可见的引起的
java内存模型
- 变量都存于主内存中,当线程1使用变量时,会将变量从主内存取出,放入工作内存
- 所有线程操作的都是自己工作内存中的变量
- 线程操作变量时,首先判断工作内存命中与否,如是则操作工作内存,否则取主内存并同步到工作内存,然后操作工作内存
问题:该机制下如果多线程同时操作,则会存在工作内存与主内存不统一,导致所谓的共享变量不可见的问题
各种锁方案
1 Synchronized 解决共享变量同步问题
- 线程进入synchronized块,java会清除工作内存中变量的内容,从主内存获取,同步到工作内存中
- 操作完毕以后,释放锁时,会将工作内存中的内容同步到主内存中;
问题:多个线程争抢sync临界区资源,会导致线程上下文切换,增加线程调度的开销
2 Volatile 共享变量不可见的轻量级解决方案
- 解决问题一:写入volatile变量 等同于 离开synchronized块,java会保证立即会刷新到主内存中
- 解决问题二:读取volatile变量 等同于 进入synchronized块 ,清除工作内存中变量的内容,从主内存获取,同步到工作内存中
问题:不能解决依赖变量a本身: a=a+1(错误),获取=>修改=>写入 这3步本身不是原子性的,不能保证线程安
3 CAS 解决线程获取不到锁,阻塞等待的问题
compareAndSet(expect,update): 如果符合当前预期值,则可以更新成update值,否则返回。
线程执行cas方法以后,可以选择循环等待,或者跳过等待等方式,避免阻塞等待
4 AtomicLong:针对单个变量的原子性操作,多个线程间共享,保证线程安全
通过volatile变量+cas方式,实现单个变量的原子性操作
适合场景:单进程全局计数器,或者累加器
问题:多个线程操作AtomicLong,都是在争抢同一个原子变量,性能不高。
可以使用LongAdder解决
结语
以上介绍了各种锁原理和适用场景,抛砖引玉,大家可以在平时编码对应的使用。
本文暂时没有评论,来添加一个吧(●'◡'●)